Skip to content

Instantly share code, notes, and snippets.

@ryanfb
Created April 16, 2024 15:39
Show Gist options
  • Select an option

  • Save ryanfb/84502d3f9fde8d91fa2339124354b785 to your computer and use it in GitHub Desktop.

Select an option

Save ryanfb/84502d3f9fde8d91fa2339124354b785 to your computer and use it in GitHub Desktop.
Diff of current Truth Social code release against previous 2022 code release
This file has been truncated, but you can view the full file.
diff -ru truth-old/opensource/.buildpacks truth-new/opensource/.buildpacks
--- truth-old/opensource/.buildpacks 2022-06-08 09:15:38
+++ truth-new/opensource/.buildpacks 2024-04-01 14:59:13
@@ -1,3 +1,3 @@
https://github.com/heroku/heroku-buildpack-apt
https://github.com/Scalingo/ffmpeg-buildpack
-https://github.com/Scalingo/ruby-buildpack
+https://github.com/heroku/heroku-buildpack-ruby
Only in truth-new/opensource: .bundle
Only in truth-new/opensource: .circleci
Only in truth-old/opensource: .env.production.sample
Only in truth-new/opensource: .envrc
diff -ru truth-old/opensource/.eslintrc.js truth-new/opensource/.eslintrc.js
--- truth-old/opensource/.eslintrc.js 2022-06-08 09:15:38
+++ truth-new/opensource/.eslintrc.js 2024-04-01 14:59:13
@@ -1,6 +1,10 @@
module.exports = {
root: true,
+ extends: [
+ 'plugin:import/typescript',
+ ],
+
env: {
browser: true,
node: true,
@@ -36,6 +40,7 @@
},
'import/extensions': [
'.js',
+ '.ts',
],
'import/ignore': [
'node_modules',
@@ -183,6 +188,7 @@
'always',
{
js: 'never',
+ ts: 'never',
},
],
'import/newline-after-import': 'error',
Only in truth-new/opensource: .github
diff -ru truth-old/opensource/.gitignore truth-new/opensource/.gitignore
--- truth-old/opensource/.gitignore 2022-06-08 09:15:38
+++ truth-new/opensource/.gitignore 2024-04-01 14:59:13
@@ -14,23 +14,22 @@
/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
+.byebug_history
.eslintcache
/log/*
!/log/.keep
/tmp
/coverage
/public/system
-/public/assets
-/public/packs
-/public/packs-test
.env
.env.production
.env.development
.env.test
+.byebug_history
/node_modules/
/build/
/scripts/
-# Ignore Capistrano customizations
+
/config/deploy/*
# Ignore Vagrant files
@@ -81,4 +80,4 @@
/opensource/
# Ignore generated test data file
-spec/support/examples/lib/csvs/accounts_for_suspension.csv
\ No newline at end of file
+spec/support/examples/lib/csvs/accounts_for_suspension.csv
diff -ru truth-old/opensource/.rubocop.yml truth-new/opensource/.rubocop.yml
--- truth-old/opensource/.rubocop.yml 2022-06-08 09:15:38
+++ truth-new/opensource/.rubocop.yml 2024-04-12 09:09:08
@@ -16,7 +16,10 @@
- 'vendor/**/*'
- 'lib/json_ld/*'
- 'lib/templates/**/*'
+ - 'lib/proto/*'
+ SuggestExtensions: false
+
Bundler/OrderedGems:
Enabled: false
@@ -49,6 +52,12 @@
Lint/DuplicateElsifCondition:
Enabled: true
+Lint/MissingSuper:
+ Exclude:
+ - 'app/services/chat_service.rb'
+ - 'app/services/chat_message_service.rb'
+ - 'app/services/geo_service.rb'
+
Lint/MixedRegexpCaptureTypes:
Enabled: true
@@ -72,6 +81,7 @@
Exclude:
- 'lib/tasks/**/*'
- 'lib/mastodon/*_cli.rb'
+ - '**/*_spec.rb'
Metrics/BlockNesting:
Max: 3
@@ -291,6 +301,9 @@
Style/SymbolArray:
Enabled: false
+
+Style/TrailingCommaInArguments:
+ EnforcedStyleForMultiline: 'comma'
Style/TrailingCommaInArrayLiteral:
EnforcedStyleForMultiline: 'comma'
Only in truth-new/opensource: .vagrant
diff -ru truth-old/opensource/Brewfile truth-new/opensource/Brewfile
--- truth-old/opensource/Brewfile 2022-06-08 09:15:38
+++ truth-new/opensource/Brewfile 2023-05-05 13:42:02
@@ -9,3 +9,5 @@
brew "postgresql"
brew "redis"
brew "direnv"
+brew "imagemagick"
+brew "ffmpeg"
diff -ru truth-old/opensource/Brewfile.lock.json truth-new/opensource/Brewfile.lock.json
--- truth-old/opensource/Brewfile.lock.json 2022-06-08 09:15:38
+++ truth-new/opensource/Brewfile.lock.json 2023-05-05 13:42:02
@@ -2,87 +2,87 @@
"entries": {
"tap": {
"homebrew/bundle": {
- "revision": "4b703e446be1d848521b0bdf3e85f36bb3aae1f9"
+ "revision": "8ec900f210c925f4b4731b0f359b0808ca132285"
},
"homebrew/core": {
- "revision": "18c5a8c7d1ed4d58a80c1b3d5485c26f290eaa01"
+ "revision": "b0fa68871ce4b23a8ea225529e5bc4aea15bac95"
}
},
"brew": {
"protobuf-c": {
- "version": "1.4.0_1",
+ "version": "1.4.1_1",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_monterey": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:4a3986d128583d41b29e369bfddeff1e369267441797b71776b8567b4eac5702",
- "sha256": "4a3986d128583d41b29e369bfddeff1e369267441797b71776b8567b4eac5702"
+ "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:201a08aabe9bc83897b908019d7dd8aba6dcddf46224eb15bbccdd5f70f6a21b",
+ "sha256": "201a08aabe9bc83897b908019d7dd8aba6dcddf46224eb15bbccdd5f70f6a21b"
},
"arm64_big_sur": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:8e855e301d3e6f20acb9b79f8e86ed46cba43790d03a2a82b2de7024abb721ec",
- "sha256": "8e855e301d3e6f20acb9b79f8e86ed46cba43790d03a2a82b2de7024abb721ec"
+ "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:48ea3989f31b6f44c8170479f5115064ed32ccd4ccf6784ea4ad254697d4f53e",
+ "sha256": "48ea3989f31b6f44c8170479f5115064ed32ccd4ccf6784ea4ad254697d4f53e"
},
"monterey": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:1d380b543cfaed179de2a482212975c9bc7219da96aa939148f9c6a6a30e170c",
- "sha256": "1d380b543cfaed179de2a482212975c9bc7219da96aa939148f9c6a6a30e170c"
+ "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:eeb51fce7f9a32e9c64ed31ffaa0c9e1fe747b0e047065fcd7e69cc6361b039c",
+ "sha256": "eeb51fce7f9a32e9c64ed31ffaa0c9e1fe747b0e047065fcd7e69cc6361b039c"
},
"big_sur": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:c89d06a0c0b555379f137f448cd8d25dd0a476d417ab277c572fd07c6faf0275",
- "sha256": "c89d06a0c0b555379f137f448cd8d25dd0a476d417ab277c572fd07c6faf0275"
+ "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:06b3fc06f5fe8b09353ac6aa106373833d897a960bb607a6caf84ba0043634ac",
+ "sha256": "06b3fc06f5fe8b09353ac6aa106373833d897a960bb607a6caf84ba0043634ac"
},
"catalina": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:55732600c0f049e6b40bee2751dfacaadd79d62a72f6f843897e25d129cbd47f",
- "sha256": "55732600c0f049e6b40bee2751dfacaadd79d62a72f6f843897e25d129cbd47f"
+ "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:5c3d841771a3527b3c118abb738b2ab04345de884588cf313d8ed14fe8514288",
+ "sha256": "5c3d841771a3527b3c118abb738b2ab04345de884588cf313d8ed14fe8514288"
},
"x86_64_linux": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:a52bfbd47abd15484c307ae0e9d11d93bf9c98606dabaa893b75952e9db80a28",
- "sha256": "a52bfbd47abd15484c307ae0e9d11d93bf9c98606dabaa893b75952e9db80a28"
+ "url": "https://ghcr.io/v2/homebrew/core/protobuf-c/blobs/sha256:4fd6aa2c3972f3b24248fb0a75638c61dc658cd0c2bc3005b088715e68f6a106",
+ "sha256": "4fd6aa2c3972f3b24248fb0a75638c61dc658cd0c2bc3005b088715e68f6a106"
}
}
}
},
"gcc": {
- "version": "11.2.0_3",
+ "version": "11.3.0_2",
"bottle": {
"rebuild": 1,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_monterey": {
"cellar": "/opt/homebrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:2d179246426328ee69b94a25b8bd4c25caeff0699b5ecb4b3d258fe4efd3673e",
- "sha256": "2d179246426328ee69b94a25b8bd4c25caeff0699b5ecb4b3d258fe4efd3673e"
+ "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:330f9db4ca60cf49809b8bb6ed0307b991330ff0184d8989e1e9fcf31c9b557d",
+ "sha256": "330f9db4ca60cf49809b8bb6ed0307b991330ff0184d8989e1e9fcf31c9b557d"
},
"arm64_big_sur": {
"cellar": "/opt/homebrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:9dbb002aa1aab75071fe1a5432fd3ee61378d711aebe0d35d0ca7226a4225451",
- "sha256": "9dbb002aa1aab75071fe1a5432fd3ee61378d711aebe0d35d0ca7226a4225451"
+ "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:ec9f983bdd7c9d8a9f383c54388d5121a51824710b78b571de892c4d773dfa06",
+ "sha256": "ec9f983bdd7c9d8a9f383c54388d5121a51824710b78b571de892c4d773dfa06"
},
"monterey": {
"cellar": "/usr/local/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:198f5312ecfe6fc6437b55e2fb3bb380e8c597ae6fa255f8f7d0be90306e7601",
- "sha256": "198f5312ecfe6fc6437b55e2fb3bb380e8c597ae6fa255f8f7d0be90306e7601"
+ "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:5624a8566fb118edbcacd5dba3b2bdd612ea6bc8fa24b3994b62becb0b2429fe",
+ "sha256": "5624a8566fb118edbcacd5dba3b2bdd612ea6bc8fa24b3994b62becb0b2429fe"
},
"big_sur": {
"cellar": "/usr/local/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:d2d4543675948c7adf3f1d4934dc651b864f66d5dad6fb3c8bdcfc6f5eef42e6",
- "sha256": "d2d4543675948c7adf3f1d4934dc651b864f66d5dad6fb3c8bdcfc6f5eef42e6"
+ "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:b8d94950b02fc77ef7e018dc7b0b69ec00b826a0bd33da32deb4214817a632d3",
+ "sha256": "b8d94950b02fc77ef7e018dc7b0b69ec00b826a0bd33da32deb4214817a632d3"
},
"catalina": {
"cellar": "/usr/local/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:e721b6a3195d2a1e73e4c12d34d0138bc5ebe6a37fb1a8d63ad733316e944c59",
- "sha256": "e721b6a3195d2a1e73e4c12d34d0138bc5ebe6a37fb1a8d63ad733316e944c59"
+ "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:efd0048f48f5bde84a6b23fbddb29b4d9f05a0c9753799a9d02c65bfddb6c7c6",
+ "sha256": "efd0048f48f5bde84a6b23fbddb29b4d9f05a0c9753799a9d02c65bfddb6c7c6"
},
"x86_64_linux": {
- "cellar": "/home/linuxbrew/.linuxbrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:3717134ab0f56e7eeb167c4f4a993c81329d6c1248dae5ee6e39f59cfdfa0eee",
- "sha256": "3717134ab0f56e7eeb167c4f4a993c81329d6c1248dae5ee6e39f59cfdfa0eee"
+ "cellar": ":any_skip_relocation",
+ "url": "https://ghcr.io/v2/homebrew/core/gcc/blobs/sha256:e826c10b577ca561cdcef55042c426bc7aabb4a937e5e2aab66c0f21d87c79f5",
+ "sha256": "e826c10b577ca561cdcef55042c426bc7aabb4a937e5e2aab66c0f21d87c79f5"
}
}
}
@@ -137,182 +137,250 @@
}
},
"shared-mime-info": {
- "version": "2.1",
+ "version": "2.2",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_monterey": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:49fd4c8b0f7cb6b3d45be48968613f8f26b0bded7f7c55b9e978c11d94efb513",
- "sha256": "49fd4c8b0f7cb6b3d45be48968613f8f26b0bded7f7c55b9e978c11d94efb513"
+ "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:6a9e8f01389c00cf8e3d3b7fbd9dc0b95d33744fcdb8bf3dca2c3db87c0d7cd1",
+ "sha256": "6a9e8f01389c00cf8e3d3b7fbd9dc0b95d33744fcdb8bf3dca2c3db87c0d7cd1"
},
"arm64_big_sur": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:c2c98a7a02e1b23f5c7f7baafe0e4b04f22a7b1a6df73912a7450ea73c162819",
- "sha256": "c2c98a7a02e1b23f5c7f7baafe0e4b04f22a7b1a6df73912a7450ea73c162819"
+ "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:c00d8c439285648cb14490b6f4bbb2111d16bd1cf7bbd386cc16ff9b1825a04a",
+ "sha256": "c00d8c439285648cb14490b6f4bbb2111d16bd1cf7bbd386cc16ff9b1825a04a"
},
"monterey": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:eb8c22370434b81375766139e32b3d8f823a1569a8f3bccc3eaf1fe9f39f250a",
- "sha256": "eb8c22370434b81375766139e32b3d8f823a1569a8f3bccc3eaf1fe9f39f250a"
+ "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:d279f9a9dfe8d9eb3aa22388b0ae41bdd284f44b35ef40b654f8d1c04929c488",
+ "sha256": "d279f9a9dfe8d9eb3aa22388b0ae41bdd284f44b35ef40b654f8d1c04929c488"
},
"big_sur": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:4857d9f38c0f3cbf23984d60c4ec6280d84b457123d34b9c01e96f3deb8b0bb2",
- "sha256": "4857d9f38c0f3cbf23984d60c4ec6280d84b457123d34b9c01e96f3deb8b0bb2"
+ "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:3287f34793705e039a140e2614d3aafad8de654e5829515ffd3b77d024de6551",
+ "sha256": "3287f34793705e039a140e2614d3aafad8de654e5829515ffd3b77d024de6551"
},
"catalina": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:8cb87ae2f3014998ecebab2d8c37ac9ff364f1164417420c4d8778a38ca17d29",
- "sha256": "8cb87ae2f3014998ecebab2d8c37ac9ff364f1164417420c4d8778a38ca17d29"
+ "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:406f54a852d1f7ea4e0e2d065495d825cd55a6e32132e0e1572c06010bfc89b6",
+ "sha256": "406f54a852d1f7ea4e0e2d065495d825cd55a6e32132e0e1572c06010bfc89b6"
},
- "mojave": {
- "cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:786d1c053d03676c985de3a7c15d764b69626f5d12e7e36e4048055bdc36413c",
- "sha256": "786d1c053d03676c985de3a7c15d764b69626f5d12e7e36e4048055bdc36413c"
- },
"x86_64_linux": {
"cellar": "/home/linuxbrew/.linuxbrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:6099cf602b42eb8f23022b02c292b0bbdce2e22f4ff5b5e8f4d8a3c4575b298f",
- "sha256": "6099cf602b42eb8f23022b02c292b0bbdce2e22f4ff5b5e8f4d8a3c4575b298f"
+ "url": "https://ghcr.io/v2/homebrew/core/shared-mime-info/blobs/sha256:42873f1d296084b1afe36a085f96b9b4074b1337b290cc0daf9a81859cf6766a",
+ "sha256": "42873f1d296084b1afe36a085f96b9b4074b1337b290cc0daf9a81859cf6766a"
}
}
}
},
"postgresql": {
- "version": "14.1_1",
+ "version": "14.4",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_monterey": {
"cellar": "/opt/homebrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:6e6f3099ad1e64fbdc9dff2152c33a2f01743d2010330bcb34cefe13052fa228",
- "sha256": "6e6f3099ad1e64fbdc9dff2152c33a2f01743d2010330bcb34cefe13052fa228"
+ "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:148b28ec301378520e83e53869452afdb82cce0b29eae4c7966dce23a110d546",
+ "sha256": "148b28ec301378520e83e53869452afdb82cce0b29eae4c7966dce23a110d546"
},
"arm64_big_sur": {
"cellar": "/opt/homebrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:d75aee6c8beaabf4add33f0f77f150b13523ffc21f6b72fd2a3c1ea0b7095362",
- "sha256": "d75aee6c8beaabf4add33f0f77f150b13523ffc21f6b72fd2a3c1ea0b7095362"
+ "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:441b6519f16ff4b6d9bdce9c116b804d27d3c0b3537cade961ee28eec2ec89f8",
+ "sha256": "441b6519f16ff4b6d9bdce9c116b804d27d3c0b3537cade961ee28eec2ec89f8"
},
"monterey": {
"cellar": "/usr/local/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:027c8b48406c3d732241426e0f5d2caf9f48cb4d2d38610b8f5d46f0adf7a89f",
- "sha256": "027c8b48406c3d732241426e0f5d2caf9f48cb4d2d38610b8f5d46f0adf7a89f"
+ "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:1e258c37f55737787151ee3a5276e805e0aa4e30cf5d166bdc2208d0d7f812c2",
+ "sha256": "1e258c37f55737787151ee3a5276e805e0aa4e30cf5d166bdc2208d0d7f812c2"
},
"big_sur": {
"cellar": "/usr/local/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:b207e5d55b0696b3b1dd649b4496d8213c933304b2b85e1913270f0834167b7f",
- "sha256": "b207e5d55b0696b3b1dd649b4496d8213c933304b2b85e1913270f0834167b7f"
+ "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:04247388a3fcade374189d6777ff6685f4b3450cf14f90bb6859eb5e2eec4b8c",
+ "sha256": "04247388a3fcade374189d6777ff6685f4b3450cf14f90bb6859eb5e2eec4b8c"
},
"catalina": {
"cellar": "/usr/local/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:7708c5cd803ce6bc1481527ecff0ad387d7489e71a3da47140768995bed3e145",
- "sha256": "7708c5cd803ce6bc1481527ecff0ad387d7489e71a3da47140768995bed3e145"
+ "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:3fa8b21ec3952be003c0803a4d7e58d478c219e1a75a0948b93e2ffebd250e7f",
+ "sha256": "3fa8b21ec3952be003c0803a4d7e58d478c219e1a75a0948b93e2ffebd250e7f"
},
"x86_64_linux": {
"cellar": "/home/linuxbrew/.linuxbrew/Cellar",
- "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:37477f8fd9f0fed2956558ad9f7169123200e6761a65936e8732a3a88d95c3ca",
- "sha256": "37477f8fd9f0fed2956558ad9f7169123200e6761a65936e8732a3a88d95c3ca"
+ "url": "https://ghcr.io/v2/homebrew/core/postgresql/blobs/sha256:431a89f854eb55b6eeed149515ea1876d0a425bede3c512cff40e49491223062",
+ "sha256": "431a89f854eb55b6eeed149515ea1876d0a425bede3c512cff40e49491223062"
}
}
}
},
"redis": {
- "version": "6.2.6",
+ "version": "7.0.3",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_monterey": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:a656500c3b5762c7cfe03d587a4fa08c5df4568783d167555962d850e8cab3c3",
- "sha256": "a656500c3b5762c7cfe03d587a4fa08c5df4568783d167555962d850e8cab3c3"
+ "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:4cc1d45e351961e95e782e66c00661316f22d580826f1703c2c59907c83e1dba",
+ "sha256": "4cc1d45e351961e95e782e66c00661316f22d580826f1703c2c59907c83e1dba"
},
"arm64_big_sur": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:846aada68ca07b36d58fd620ed5d52ae67a759526c5da27042748363bfdb6271",
- "sha256": "846aada68ca07b36d58fd620ed5d52ae67a759526c5da27042748363bfdb6271"
+ "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:69d4b633d35eac570aba6c16aacb3e597c16c1f02a5c730c1785ae4dbad3b0a2",
+ "sha256": "69d4b633d35eac570aba6c16aacb3e597c16c1f02a5c730c1785ae4dbad3b0a2"
},
"monterey": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:ac30519a604ff014e3903893ddca6c563c134002fec58df3613632e42c4d117c",
- "sha256": "ac30519a604ff014e3903893ddca6c563c134002fec58df3613632e42c4d117c"
+ "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:b0ceaa7592468ee103656390217caa61259a0542c3289d413e442b6237d25fe5",
+ "sha256": "b0ceaa7592468ee103656390217caa61259a0542c3289d413e442b6237d25fe5"
},
"big_sur": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:246f73498993a2a0c6c4326a298d2fcc3da6d61904ad09a631aa9c63a6800f76",
- "sha256": "246f73498993a2a0c6c4326a298d2fcc3da6d61904ad09a631aa9c63a6800f76"
+ "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:28bc30760d01dac125aea13c2c3814995728d63025be4df8e92aefe2bdd6fe71",
+ "sha256": "28bc30760d01dac125aea13c2c3814995728d63025be4df8e92aefe2bdd6fe71"
},
"catalina": {
"cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:ff93a763d622cc9130c09fa9ce2ec7236f91562667eaa5c304fcf175c1253746",
- "sha256": "ff93a763d622cc9130c09fa9ce2ec7236f91562667eaa5c304fcf175c1253746"
+ "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:3387ab3983d8c93d0f57d8796dcc00db3750184c9bdbb1374e468be172de07f4",
+ "sha256": "3387ab3983d8c93d0f57d8796dcc00db3750184c9bdbb1374e468be172de07f4"
},
- "mojave": {
- "cellar": ":any",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:57842762aad1434f8b511f603364b4528f0545f7d768c9387b362011351cda2b",
- "sha256": "57842762aad1434f8b511f603364b4528f0545f7d768c9387b362011351cda2b"
- },
"x86_64_linux": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:8398fc05ef8eb1ea3d7b26844e3a314a948b3d0d4fb937a00c6c62f0abbe340a",
- "sha256": "8398fc05ef8eb1ea3d7b26844e3a314a948b3d0d4fb937a00c6c62f0abbe340a"
+ "url": "https://ghcr.io/v2/homebrew/core/redis/blobs/sha256:823b2c71e5656342095f1af7bcb4d43eef9014717b3687a2e6248a7516872970",
+ "sha256": "823b2c71e5656342095f1af7bcb4d43eef9014717b3687a2e6248a7516872970"
}
}
}
},
"direnv": {
- "version": "2.30.3",
+ "version": "2.32.1",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_monterey": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:0a2bf97696f0e57e713db8f39dcff719fa17e0512b6ad14a7657a1c946943a85",
- "sha256": "0a2bf97696f0e57e713db8f39dcff719fa17e0512b6ad14a7657a1c946943a85"
+ "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:567a8c1ca45ffae17d4901b0aa729be8866b6cb93ee8224da98cacf36f73ed69",
+ "sha256": "567a8c1ca45ffae17d4901b0aa729be8866b6cb93ee8224da98cacf36f73ed69"
},
"arm64_big_sur": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:41d4f105cdef28417dae6c248a5819709967897071c34ed63fa5432644d944f2",
- "sha256": "41d4f105cdef28417dae6c248a5819709967897071c34ed63fa5432644d944f2"
+ "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:b21230f43123e6b1a832f87d30f040d9f4684bb19f62f69f651bb24ae1cfaaab",
+ "sha256": "b21230f43123e6b1a832f87d30f040d9f4684bb19f62f69f651bb24ae1cfaaab"
},
"monterey": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:761499a99dc029d5cafe075105827c4897d5e45dd53cfa7bf86ea51fc4f1afaf",
- "sha256": "761499a99dc029d5cafe075105827c4897d5e45dd53cfa7bf86ea51fc4f1afaf"
+ "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:68f7b9093d44fdef4210ffeaa8f88e8fa27bef356b4c8b2d4fc7749aab1d2614",
+ "sha256": "68f7b9093d44fdef4210ffeaa8f88e8fa27bef356b4c8b2d4fc7749aab1d2614"
},
"big_sur": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:73fc3e19b391c97806c44d2f2b38b5ddc28742d656ab6ca013371acc6cabd5bc",
- "sha256": "73fc3e19b391c97806c44d2f2b38b5ddc28742d656ab6ca013371acc6cabd5bc"
+ "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:f5dc03f040b2638a14e30c9fdeaaed616084539c0360fc916b53c3c8206259c4",
+ "sha256": "f5dc03f040b2638a14e30c9fdeaaed616084539c0360fc916b53c3c8206259c4"
},
"catalina": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:77c87b8f6ee51b65514b5688babcd83117b75feb2b40b0489bd0649cdeb3f3cb",
- "sha256": "77c87b8f6ee51b65514b5688babcd83117b75feb2b40b0489bd0649cdeb3f3cb"
+ "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:2edc8e221d28db3da039490d4728d3c5ac7ce38d5274418b4d5f0ca31dfc0b28",
+ "sha256": "2edc8e221d28db3da039490d4728d3c5ac7ce38d5274418b4d5f0ca31dfc0b28"
},
"x86_64_linux": {
"cellar": ":any_skip_relocation",
- "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:b18ff46bf3e0b18eace8b2a0754829555a991d58d6fe3f76681a8a057c48a04c",
- "sha256": "b18ff46bf3e0b18eace8b2a0754829555a991d58d6fe3f76681a8a057c48a04c"
+ "url": "https://ghcr.io/v2/homebrew/core/direnv/blobs/sha256:c0356bf7cc43d0a1eb7777e7ed390f47f9dd8fb51cc480c8fb87fcde5fba1b4a",
+ "sha256": "c0356bf7cc43d0a1eb7777e7ed390f47f9dd8fb51cc480c8fb87fcde5fba1b4a"
}
}
}
+ },
+ "imagemagick": {
+ "version": "7.1.0-43",
+ "bottle": {
+ "rebuild": 0,
+ "root_url": "https://ghcr.io/v2/homebrew/core",
+ "files": {
+ "arm64_monterey": {
+ "cellar": "/opt/homebrew/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:0400965c5e3292014220eccc4c86c781322de2656a73521223dad47a5995d9d2",
+ "sha256": "0400965c5e3292014220eccc4c86c781322de2656a73521223dad47a5995d9d2"
+ },
+ "arm64_big_sur": {
+ "cellar": "/opt/homebrew/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:c8b12081678eddd29bdd48e64d9920159f0ddc4c35e9b5dabc4f32b954b4d111",
+ "sha256": "c8b12081678eddd29bdd48e64d9920159f0ddc4c35e9b5dabc4f32b954b4d111"
+ },
+ "monterey": {
+ "cellar": "/usr/local/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:7ba74e818a3c320d0246d9d01f063ae1acb1bc7b9682afdb8477a3f9760cf003",
+ "sha256": "7ba74e818a3c320d0246d9d01f063ae1acb1bc7b9682afdb8477a3f9760cf003"
+ },
+ "big_sur": {
+ "cellar": "/usr/local/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:52a890f084a4dbe6da65b9a93d626b53d970ebccab4b5e7ba72a6f025be07f49",
+ "sha256": "52a890f084a4dbe6da65b9a93d626b53d970ebccab4b5e7ba72a6f025be07f49"
+ },
+ "catalina": {
+ "cellar": "/usr/local/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:f1f5c94945b0da6263fd55f206eebf04abfb24745f395f7574b81e2a03e782c5",
+ "sha256": "f1f5c94945b0da6263fd55f206eebf04abfb24745f395f7574b81e2a03e782c5"
+ },
+ "x86_64_linux": {
+ "cellar": "/home/linuxbrew/.linuxbrew/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/imagemagick/blobs/sha256:2b7e46bd4f6282f30ae1bd0bbe700460a057df23ea1697f02fd111cca5188f39",
+ "sha256": "2b7e46bd4f6282f30ae1bd0bbe700460a057df23ea1697f02fd111cca5188f39"
+ }
+ }
+ }
+ },
+ "ffmpeg": {
+ "version": "5.0.1_3",
+ "bottle": {
+ "rebuild": 0,
+ "root_url": "https://ghcr.io/v2/homebrew/core",
+ "files": {
+ "arm64_monterey": {
+ "cellar": "/opt/homebrew/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:6aa7fa8dbc5fb950f1ef81c31e5c3af52d92c616ea9a4b46e58b42c51a0ba7d7",
+ "sha256": "6aa7fa8dbc5fb950f1ef81c31e5c3af52d92c616ea9a4b46e58b42c51a0ba7d7"
+ },
+ "arm64_big_sur": {
+ "cellar": "/opt/homebrew/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:a494fef2d5a93ecdadfce8530964af6ddcdb8662795bb7aa35ef8f8d8f659a01",
+ "sha256": "a494fef2d5a93ecdadfce8530964af6ddcdb8662795bb7aa35ef8f8d8f659a01"
+ },
+ "monterey": {
+ "cellar": "/usr/local/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:b835b65ef6d4b85e36b7a315133fd9310a4ab6184caef6e8c99174d4aeec7bbb",
+ "sha256": "b835b65ef6d4b85e36b7a315133fd9310a4ab6184caef6e8c99174d4aeec7bbb"
+ },
+ "big_sur": {
+ "cellar": "/usr/local/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:a65289290fb40e981887568f2711357402f2e9e7e42f57e2c4d3984f11b36f7a",
+ "sha256": "a65289290fb40e981887568f2711357402f2e9e7e42f57e2c4d3984f11b36f7a"
+ },
+ "catalina": {
+ "cellar": "/usr/local/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:d35f1a769b57ff7180076d53af7c1602ff7e3d3f29f81d6e5a6cb1a90cbc6a3a",
+ "sha256": "d35f1a769b57ff7180076d53af7c1602ff7e3d3f29f81d6e5a6cb1a90cbc6a3a"
+ },
+ "x86_64_linux": {
+ "cellar": "/home/linuxbrew/.linuxbrew/Cellar",
+ "url": "https://ghcr.io/v2/homebrew/core/ffmpeg/blobs/sha256:d020ef50ab876425fa9d9555473b3925da14fe80365a7dc39f53e98e5b7960de",
+ "sha256": "d020ef50ab876425fa9d9555473b3925da14fe80365a7dc39f53e98e5b7960de"
+ }
+ }
+ }
}
}
},
"system": {
"macos": {
"monterey": {
- "HOMEBREW_VERSION": "3.3.13",
- "HOMEBREW_PREFIX": "/usr/local",
- "Homebrew/homebrew-core": "18c5a8c7d1ed4d58a80c1b3d5485c26f290eaa01",
- "CLT": "12.5.0.22.11",
- "Xcode": "12.5.1",
- "macOS": "12.2"
+ "HOMEBREW_VERSION": "3.5.5",
+ "HOMEBREW_PREFIX": "/opt/homebrew",
+ "Homebrew/homebrew-core": "b0fa68871ce4b23a8ea225529e5bc4aea15bac95",
+ "CLT": "13.0.0.0.1.1627064638",
+ "Xcode": "13.2.1",
+ "macOS": "12.3"
}
}
}
Only in truth-new/opensource: CHANGELOG.md
Only in truth-new/opensource: CONTRIBUTING.md
Only in truth-old/opensource: Capfile
diff -ru truth-old/opensource/Gemfile truth-new/opensource/Gemfile
--- truth-old/opensource/Gemfile 2022-06-08 09:15:38
+++ truth-new/opensource/Gemfile 2024-04-05 09:07:34
@@ -15,24 +15,25 @@
gem 'hamlit-rails', '~> 0.2'
gem 'pg', '~> 1.2'
gem 'makara', '~> 0.5'
-gem 'pghero', '~> 2.8'
gem 'dotenv-rails', '~> 2.7'
+gem 'acts_as_list', '~> 1.1'
gem 'aws-sdk-s3', '~> 1.96', require: false
gem 'activerecord-import'
gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false
-gem 'paperclip', '~> 6.0'
+gem 'paperclip', '~> 6.0.0'
gem 'blurhash', '~> 0.1'
+gem 'composite_primary_keys', '~> 13.0'
gem 'active_model_serializers', '~> 0.10'
-gem 'addressable', '~> 2.7'
+gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.6.0', require: false
gem 'browser'
gem 'charlock_holmes', '~> 0.7.7'
gem 'iso-639'
-gem 'chewy', '~> 5.2'
-gem 'cld3', '~> 3.4.2'
+gem 'chewy', '~> 7.2.4'
+gem 'cld3', '~> 3.5.0'
gem 'devise', '~> 4.8'
gem 'devise-two-factor', '~> 4.0'
@@ -46,10 +47,14 @@
gem 'omniauth', '~> 1.9'
gem 'omniauth-rails_csrf_protection', '~> 0.1'
+gem 'amqp', '~> 1.8.0'
+gem 'bunny', '~> 2.19.0'
gem 'color_diff', '~> 0.1'
gem 'discard', '~> 1.2'
gem 'doorkeeper', '~> 5.5'
gem 'ed25519', '~> 1.2'
+gem 'fabrication', '~> 2.22'
+gem 'faker', '~> 2.18'
gem 'fast_blank', '~> 1.0'
gem 'fastimage'
gem 'hiredis', '~> 0.6'
@@ -59,19 +64,20 @@
gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 1.5.0'
gem 'idn-ruby', require: 'idn'
+gem 'jwe', '~> 0.4.0'
gem 'jwt', '~> 2.2'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.11'
-gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.11'
gem 'ox', '~> 2.14'
+gem 'panko_serializer', '~> 0.7.7'
gem 'parslet'
-gem 'parallel', '~> 1.20'
+gem 'phonelib', '~> 0.8.7'
gem 'posix-spawn'
gem 'prometheus_exporter', '~> 1.0'
-gem 'google-protobuf', '~> 3.19'
+gem 'ruby_proto_schemas', git: "https://gitlab.com/tmediatech/ruby-proto-schemas.git", tag: "v4"
gem 'premailer-rails'
gem 'pundit', '~> 2.1'
gem 'rack-attack', '~> 6.5'
@@ -87,28 +93,31 @@
gem 'scenic', '~> 1.5'
gem 'sidekiq', '~> 6.2'
gem 'sidekiq-scheduler', '~> 3.1'
-gem 'sidekiq-unique-jobs', '~> 7.0'
+gem 'sidekiq-unique-jobs', '~> 7.1'
gem 'sidekiq-bulk', '~>0.2.0'
gem 'simple-navigation', '~> 4.3'
gem 'simple_form', '~> 5.1'
+gem 'sneakers', '~> 2.3', '>= 2.3.5'
+gem 'sneakers_handlers', '~> 0.0.8'
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
gem 'stoplight', '~> 2.2.1'
gem 'strong_migrations', '~> 0.7'
+gem 'swagger-blocks'
gem 'tty-prompt', '~> 0.23', require: false
gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2021'
gem 'webpacker', '~> 5.4.2'
gem 'webpush', '~> 0.3'
-gem 'webauthn', '~> 3.0.0.alpha1'
+gem 'webauthn', '~> 2.5'
gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.1'
gem 'rdf-normalize', '~> 0.4'
group :development, :test do
- gem 'fabrication', '~> 2.22'
gem 'fuubar', '~> 2.5'
gem 'i18n-tasks', '~> 0.9', require: false
+ gem 'oauth2'
gem 'pry-byebug', '~> 3.9'
gem 'pry-rails', '~> 0.3'
gem 'rspec-rails', '~> 5.0'
@@ -121,7 +130,6 @@
group :test do
gem 'capybara', '~> 3.35'
gem 'climate_control', '~> 0.2'
- gem 'faker', '~> 2.18'
gem 'microformats', '~> 4.2'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.1'
@@ -140,16 +148,11 @@
gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.4'
gem 'memory_profiler'
- gem 'rubocop', '~> 1.16', require: false
+ gem 'rubocop', '~> 1.60', require: false
gem 'rubocop-rails', '~> 2.10', require: false
+ gem 'rubocop-rspec', '~> 2.26', require: false
gem 'brakeman', '~> 5.0', require: false
gem 'bundler-audit', '~> 0.8', require: false
-
- gem 'capistrano', '~> 3.16'
- gem 'capistrano-rails', '~> 1.6'
- gem 'capistrano-rbenv', '~> 2.2'
- gem 'capistrano-yarn', '~> 2.0'
-
gem 'stackprof'
end
@@ -159,9 +162,7 @@
gem 'concurrent-ruby', require: false
gem 'connection_pool', require: false
-
-gem 'xorcist', '~> 1.1'
gem 'resolv', '~> 0.1.0'
-gem 'newrelic_rpm', '~> 7.2'
\ No newline at end of file
+gem 'newrelic_rpm', '~> 7.2'
diff -ru truth-old/opensource/Gemfile.lock truth-new/opensource/Gemfile.lock
--- truth-old/opensource/Gemfile.lock 2022-06-08 09:15:38
+++ truth-new/opensource/Gemfile.lock 2024-04-05 09:07:34
@@ -1,3 +1,11 @@
+GIT
+ remote: https://gitlab.com/tmediatech/ruby-proto-schemas.git
+ revision: c1416cc1a1716b2d46d6a0dcd4470630182e5a86
+ tag: v4
+ specs:
+ ruby_proto_schemas (0.1.0)
+ google-protobuf (~> 3.19)
+
GEM
remote: https://rubygems.org/
specs:
@@ -68,10 +76,14 @@
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
- addressable (2.7.0)
- public_suffix (>= 2.0.2, < 5.0)
- airbrussh (1.4.0)
- sshkit (>= 1.6.1, != 1.7.0)
+ acts_as_list (1.1.0)
+ activerecord (>= 4.2)
+ addressable (2.8.6)
+ public_suffix (>= 2.0.2, < 6.0)
+ amq-protocol (2.3.2)
+ amqp (1.8.0)
+ amq-protocol (>= 2.2.0)
+ eventmachine
android_key_attestation (0.3.0)
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
@@ -79,7 +91,7 @@
ast (2.4.2)
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
- awrence (1.1.1)
+ awrence (1.2.1)
aws-eventstream (1.1.1)
aws-partitions (1.465.0)
aws-sdk-core (3.114.1)
@@ -101,7 +113,7 @@
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
- bindata (2.4.8)
+ bindata (2.4.10)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
blurhash (0.1.5)
@@ -120,22 +132,10 @@
bundler-audit (0.8.0)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
+ bunny (2.19.0)
+ amq-protocol (~> 2.3, >= 2.3.1)
+ sorted_set (~> 1, >= 1.0.2)
byebug (11.1.3)
- capistrano (3.16.0)
- airbrussh (>= 1.0.0)
- i18n
- rake (>= 10.0.0)
- sshkit (>= 1.9.0)
- capistrano-bundler (2.0.1)
- capistrano (~> 3.1)
- capistrano-rails (1.6.1)
- capistrano (~> 3.1)
- capistrano-bundler (>= 1.1, < 3)
- capistrano-rbenv (2.2.0)
- capistrano (~> 3.1)
- sshkit (~> 1.3)
- capistrano-yarn (2.0.2)
- capistrano (~> 3.0)
capybara (3.35.3)
addressable
mini_mime (>= 0.1.3)
@@ -148,21 +148,23 @@
activesupport
cbor (0.5.9.6)
charlock_holmes (0.7.7)
- chewy (5.2.0)
+ chewy (7.2.6)
activesupport (>= 5.2)
- elasticsearch (>= 2.0.0)
+ elasticsearch (>= 7.12.0, < 7.14.0)
elasticsearch-dsl
chunky_png (1.4.0)
- cld3 (3.4.2)
+ cld3 (3.5.0)
ffi (>= 1.1.0, < 1.16.0)
climate_control (0.2.0)
coderay (1.1.3)
color_diff (0.1)
- concurrent-ruby (1.1.9)
+ composite_primary_keys (13.0.3)
+ activerecord (~> 6.1.0)
+ concurrent-ruby (1.1.10)
connection_pool (2.2.5)
- cose (1.0.0)
+ cose (1.2.0)
cbor (~> 0.5.9)
- openssl-signature_algorithm (~> 0.4.0)
+ openssl-signature_algorithm (~> 1.0)
crack (0.4.5)
rexml
crass (1.0.6)
@@ -198,28 +200,47 @@
railties (>= 3.2)
e2mmap (0.1.0)
ed25519 (1.2.4)
- elasticsearch (7.10.1)
- elasticsearch-api (= 7.10.1)
- elasticsearch-transport (= 7.10.1)
- elasticsearch-api (7.10.1)
+ elasticsearch (7.13.3)
+ elasticsearch-api (= 7.13.3)
+ elasticsearch-transport (= 7.13.3)
+ elasticsearch-api (7.13.3)
multi_json
- elasticsearch-dsl (0.1.9)
- elasticsearch-transport (7.10.1)
+ elasticsearch-dsl (0.1.10)
+ elasticsearch-transport (7.13.3)
faraday (~> 1)
multi_json
encryptor (3.0.0)
erubi (1.10.0)
et-orbi (1.2.4)
tzinfo
+ eventmachine (1.2.7)
excon (0.76.0)
fabrication (2.22.0)
faker (2.18.0)
i18n (>= 1.6, < 2)
- faraday (1.3.0)
+ faraday (1.10.0)
+ faraday-em_http (~> 1.0)
+ faraday-em_synchrony (~> 1.0)
+ faraday-excon (~> 1.1)
+ faraday-httpclient (~> 1.0)
+ faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
- multipart-post (>= 1.2, < 3)
- ruby2_keywords
+ faraday-net_http_persistent (~> 1.0)
+ faraday-patron (~> 1.0)
+ faraday-rack (~> 1.0)
+ faraday-retry (~> 1.0)
+ ruby2_keywords (>= 0.0.4)
+ faraday-em_http (1.0.0)
+ faraday-em_synchrony (1.0.0)
+ faraday-excon (1.1.0)
+ faraday-httpclient (1.0.1)
+ faraday-multipart (1.0.4)
+ multipart-post (~> 2)
faraday-net_http (1.0.1)
+ faraday-net_http_persistent (1.2.0)
+ faraday-patron (1.0.0)
+ faraday-rack (1.0.0)
+ faraday-retry (1.0.3)
fast_blank (1.0.0)
fastimage (2.2.4)
ffi (1.15.5)
@@ -247,7 +268,7 @@
ruby-progressbar (~> 1.4)
globalid (0.4.2)
activesupport (>= 4.2.0)
- google-protobuf (3.19.3)
+ google-protobuf (3.25.3)
hamlit (2.13.0)
temple (>= 0.8.2)
thor
@@ -261,7 +282,7 @@
concurrent-ruby (~> 1.0)
hashdiff (1.0.1)
hashie (4.1.0)
- highline (2.0.3)
+ highline (2.1.0)
hiredis (0.6.3)
hkdf (0.3.0)
htmlentities (4.3.4)
@@ -279,9 +300,9 @@
httplog (1.5.0)
rack (>= 1.0)
rainbow (>= 2.0.0)
- i18n (1.8.10)
+ i18n (1.12.0)
concurrent-ruby (~> 1.0)
- i18n-tasks (0.9.34)
+ i18n-tasks (0.9.37)
activesupport (>= 4.0.2)
ast (>= 2.1.0)
erubi
@@ -308,6 +329,7 @@
json-ld (~> 3.1)
rdf (~> 3.1)
jsonapi-renderer (0.2.2)
+ jwe (0.4.0)
jwt (2.2.2)
kaminari (1.2.1)
activesupport (>= 4.1.0)
@@ -321,6 +343,7 @@
activerecord
kaminari-core (= 1.2.1)
kaminari-core (1.2.1)
+ language_server-protocol (3.17.0.3)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.7.0)
@@ -358,14 +381,12 @@
rake
mini_mime (1.0.3)
mini_portile2 (2.5.3)
- minitest (5.14.4)
+ minitest (5.16.2)
msgpack (1.4.2)
multi_json (1.15.0)
- multipart-post (2.1.1)
+ multi_xml (0.6.0)
+ multipart-post (2.2.3)
net-ldap (0.17.0)
- net-scp (3.0.0)
- net-ssh (>= 2.6.5, < 7.0.0)
- net-ssh (6.1.0)
newrelic_rpm (7.2.0)
nio4r (2.5.7)
nokogiri (1.11.7)
@@ -373,11 +394,13 @@
racc (~> 1.4)
nokogumbo (2.0.4)
nokogiri (~> 1.8, >= 1.8.4)
- nsa (0.2.8)
- activesupport (>= 4.2, < 7)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- sidekiq (>= 3.5)
- statsd-ruby (~> 1.4, >= 1.4.0)
+ oauth2 (2.0.6)
+ faraday (>= 0.17.3, < 3.0)
+ jwt (>= 1.0, < 3.0)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ rash_alt (>= 0.4, < 1)
+ version_gem (~> 1.1)
oj (3.11.5)
omniauth (1.9.1)
hashie (>= 3.4.6)
@@ -393,9 +416,13 @@
omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.9)
openssl (2.2.0)
- openssl-signature_algorithm (0.4.0)
+ openssl-signature_algorithm (1.1.1)
+ openssl (~> 2.0)
orm_adapter (0.5.0)
ox (2.14.5)
+ panko_serializer (0.7.7)
+ activesupport
+ oj (> 3.11.0, < 4.0.0)
paperclip (6.0.0)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
@@ -405,14 +432,14 @@
parallel (1.20.1)
parallel_tests (3.7.0)
parallel
- parser (3.0.1.1)
+ parser (3.3.0.5)
ast (~> 2.4.1)
+ racc
parslet (2.0.0)
pastel (0.8.0)
tty-color (~> 0.5)
pg (1.2.3)
- pghero (2.8.1)
- activerecord (>= 5)
+ phonelib (0.8.7)
pkg-config (1.4.6)
posix-spawn (0.3.15)
premailer (1.14.2)
@@ -433,7 +460,7 @@
pry (~> 0.13.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
- public_suffix (4.0.6)
+ public_suffix (5.0.4)
puma (5.4.0)
nio4r (~> 2.0)
pundit (2.1.0)
@@ -490,6 +517,9 @@
activerecord (>= 6.0.4)
activesupport (>= 6.0.4)
i18n
+ rash_alt (0.4.12)
+ hashie (>= 3.4)
+ rbtree (0.4.5)
rdf (3.1.13)
hamster (~> 3.0)
link_header (~> 0.0, >= 0.0.8)
@@ -534,25 +564,35 @@
rspec-support (3.10.2)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
- rubocop (1.16.0)
+ rubocop (1.60.2)
+ json (~> 2.3)
+ language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
- parser (>= 3.0.0.0)
+ parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
- rexml
- rubocop-ast (>= 1.7.0, < 2.0)
+ rexml (>= 3.2.5, < 4.0)
+ rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7)
- unicode-display_width (>= 1.4.0, < 3.0)
- rubocop-ast (1.7.0)
- parser (>= 3.0.1.1)
+ unicode-display_width (>= 2.4.0, < 3.0)
+ rubocop-ast (1.30.0)
+ parser (>= 3.2.1.0)
+ rubocop-capybara (2.20.0)
+ rubocop (~> 1.41)
+ rubocop-factory_bot (2.25.1)
+ rubocop (~> 1.41)
rubocop-rails (2.10.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0)
+ rubocop-rspec (2.26.1)
+ rubocop (~> 1.40)
+ rubocop-capybara (~> 2.17)
+ rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.11.0)
ruby-saml (1.11.0)
nokogiri (>= 1.5.10)
- ruby2_keywords (0.0.4)
+ ruby2_keywords (0.0.5)
rufus-scheduler (3.7.0)
fugit (~> 1.1, >= 1.1.6)
safety_net_attestation (0.4.0)
@@ -564,8 +604,10 @@
scenic (1.5.4)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
- securecompare (1.0.0)
semantic_range (3.0.0)
+ serverengine (2.0.7)
+ sigdump (~> 0.2.2)
+ set (1.0.2)
sidekiq (6.2.1)
connection_pool (>= 2.2.2)
rack (~> 2.0)
@@ -579,11 +621,12 @@
sidekiq (>= 3)
thwait
tilt (>= 1.4.0)
- sidekiq-unique-jobs (7.0.12)
+ sidekiq-unique-jobs (7.1.25)
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
concurrent-ruby (~> 1.0, >= 1.0.5)
- sidekiq (>= 5.0, < 7.0)
- thor (>= 0.20, < 2.0)
+ sidekiq (>= 5.0, < 8.0)
+ thor (>= 0.20, < 3.0)
+ sigdump (0.2.5)
simple-navigation (4.3.0)
activesupport (>= 2.3.2)
simple_form (5.1.0)
@@ -595,6 +638,17 @@
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.2)
+ sneakers (2.11.0)
+ bunny (~> 2.12)
+ concurrent-ruby (~> 1.0)
+ rake
+ serverengine (~> 2.0.5)
+ thor
+ sneakers_handlers (0.0.8)
+ sneakers (~> 2.0)
+ sorted_set (1.0.3)
+ rbtree
+ set (~> 1.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
@@ -602,26 +656,23 @@
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
- sshkit (1.21.2)
- net-scp (>= 1.1.2)
- net-ssh (>= 2.8.0)
stackprof (0.2.17)
- statsd-ruby (1.5.0)
stoplight (2.2.1)
strong_migrations (0.7.6)
activerecord (>= 5)
+ swagger-blocks (3.0.0)
temple (0.8.2)
- terminal-table (3.0.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
thor (1.1.0)
thwait (0.2.0)
e2mmap
tilt (2.0.10)
- tpm-key_attestation (0.9.0)
+ tpm-key_attestation (0.10.0)
bindata (~> 2.4)
- openssl-signature_algorithm (~> 0.4.0)
+ openssl-signature_algorithm (~> 1.0)
tty-color (0.6.0)
tty-cursor (0.7.1)
tty-prompt (0.23.1)
@@ -635,27 +686,27 @@
twitter-text (3.1.0)
idn-ruby
unf (~> 0.1.0)
- tzinfo (2.0.4)
+ tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2021.1)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
- unicode-display_width (1.7.0)
+ unicode-display_width (2.5.0)
uniform_notifier (1.14.1)
+ version_gem (1.1.0)
warden (1.2.9)
rack (>= 2.0.9)
- webauthn (3.0.0.alpha1)
+ webauthn (2.5.1)
android_key_attestation (~> 0.3.0)
awrence (~> 1.1)
bindata (~> 2.4)
cbor (~> 0.5.9)
- cose (~> 1.0)
- openssl (~> 2.0)
+ cose (~> 1.1)
+ openssl (~> 2.2)
safety_net_attestation (~> 0.4.0)
- securecompare (~> 1.0)
- tpm-key_attestation (~> 0.9.0)
+ tpm-key_attestation (~> 0.10.0)
webmock (3.13.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
@@ -673,10 +724,9 @@
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wisper (2.0.1)
- xorcist (1.1.2)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.4.2)
+ zeitwerk (2.6.0)
PLATFORMS
ruby
@@ -685,7 +735,9 @@
active_model_serializers (~> 0.10)
active_record_query_trace (~> 1.8)
activerecord-import
- addressable (~> 2.7)
+ acts_as_list (~> 1.1)
+ addressable (~> 2.8)
+ amqp (~> 1.8.0)
annotate (~> 3.1)
aws-sdk-s3 (~> 1.96)
better_errors (~> 2.9)
@@ -696,16 +748,14 @@
browser
bullet (~> 6.1)
bundler-audit (~> 0.8)
- capistrano (~> 3.16)
- capistrano-rails (~> 1.6)
- capistrano-rbenv (~> 2.2)
- capistrano-yarn (~> 2.0)
+ bunny (~> 2.19.0)
capybara (~> 3.35)
charlock_holmes (~> 0.7.7)
- chewy (~> 5.2)
- cld3 (~> 3.4.2)
+ chewy (~> 7.2.4)
+ cld3 (~> 3.5.0)
climate_control (~> 0.2)
color_diff (~> 0.1)
+ composite_primary_keys (~> 13.0)
concurrent-ruby
connection_pool
devise (~> 4.8)
@@ -722,7 +772,6 @@
fog-core (<= 2.1.0)
fog-openstack (~> 0.3)
fuubar (~> 2.5)
- google-protobuf (~> 3.19)
hamlit-rails (~> 0.2)
hiredis (~> 0.6)
htmlentities (~> 4.3)
@@ -734,6 +783,7 @@
iso-639
json-ld
json-ld-preloaded (~> 3.1)
+ jwe (~> 0.4.0)
jwt (~> 2.2)
kaminari (~> 1.2)
letter_opener (~> 1.7)
@@ -748,7 +798,7 @@
net-ldap (~> 0.17)
newrelic_rpm (~> 7.2)
nokogiri (~> 1.11)
- nsa (~> 0.2)
+ oauth2
oj (~> 3.11)
omniauth (~> 1.9)
omniauth-cas (~> 2.0)
@@ -756,12 +806,12 @@
omniauth-saml (~> 1.10)
openssl (~> 2.2.0)
ox (~> 2.14)
- paperclip (~> 6.0)
- parallel (~> 1.20)
+ panko_serializer (~> 0.7.7)
+ paperclip (~> 6.0.0)
parallel_tests (~> 3.7)
parslet
pg (~> 1.2)
- pghero (~> 2.8)
+ phonelib (~> 0.8.7)
pkg-config (~> 1.4)
posix-spawn
premailer-rails
@@ -787,35 +837,39 @@
rspec-rails (~> 5.0)
rspec-sidekiq (~> 3.1)
rspec_junit_formatter (~> 0.4)
- rubocop (~> 1.16)
+ rubocop (~> 1.60)
rubocop-rails (~> 2.10)
+ rubocop-rspec (~> 2.26)
ruby-progressbar (~> 1.11)
+ ruby_proto_schemas!
sanitize (~> 5.2)
scenic (~> 1.5)
sidekiq (~> 6.2)
sidekiq-bulk (~> 0.2.0)
sidekiq-scheduler (~> 3.1)
- sidekiq-unique-jobs (~> 7.0)
+ sidekiq-unique-jobs (~> 7.1)
simple-navigation (~> 4.3)
simple_form (~> 5.1)
simplecov (~> 0.21)
+ sneakers (~> 2.3, >= 2.3.5)
+ sneakers_handlers (~> 0.0.8)
sprockets (~> 3.7.2)
sprockets-rails (~> 3.2)
stackprof
stoplight (~> 2.2.1)
strong_migrations (~> 0.7)
+ swagger-blocks
thor (~> 1.1)
tty-prompt (~> 0.23)
twitter-text (~> 3.1.0)
tzinfo-data (~> 1.2021)
- webauthn (~> 3.0.0.alpha1)
+ webauthn (~> 2.5)
webmock (~> 3.13)
webpacker (~> 5.4.2)
webpush (~> 0.3)
- xorcist (~> 1.1)
RUBY VERSION
ruby 2.7.2p137
BUNDLED WITH
- 2.2.29
+ 2.3.18
diff -ru truth-old/opensource/Rakefile truth-new/opensource/Rakefile
--- truth-old/opensource/Rakefile 2022-06-08 09:15:38
+++ truth-new/opensource/Rakefile 2024-04-01 14:59:13
@@ -1,5 +1,5 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
-# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+# for example lib/tasks/groups.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
diff -ru truth-old/opensource/app/chewy/accounts_index.rb truth-new/opensource/app/chewy/accounts_index.rb
--- truth-old/opensource/app/chewy/accounts_index.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/chewy/accounts_index.rb 2024-04-01 14:59:13
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class AccountsIndex < Chewy::Index
- settings index: { refresh_interval: '5m' }, number_of_shards: '12', analysis: {
+ settings index: { refresh_interval: '1m' }, number_of_shards: '12', analysis: {
analyzer: {
content: {
tokenizer: 'whitespace',
@@ -12,8 +12,26 @@
tokenizer: 'edge_ngram',
filter: %w(lowercase asciifolding cjk_width),
},
+
+ exact: {
+ tokenizer: 'keyword',
+ filter: %w(lowercase asciifolding cjk_width),
+ },
+
+ phone: {
+ tokenizer: 'pattern',
+ filter: %w(phone),
+ },
},
+ filter: {
+ phone: {
+ type: 'pattern_capture',
+ preserve_original: false,
+ patterns: ['(\d+(\d{10}))'],
+ },
+ },
+
tokenizer: {
edge_ngram: {
type: 'edge_ngram',
@@ -23,23 +41,34 @@
},
}
- define_type ::Account.searchable.includes(:account_stat), delete_if: ->(account) {
- account.destroyed? || !account.searchable?
- } do
- root date_detection: false do
- field :id, type: 'long'
+ index_scope ::Account.includes(:account_follower, :account_following, :account_status), delete_if: ->(account) { account.destroyed? || !account.searchable? }
- field :display_name, type: 'text', analyzer: 'content' do
- field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
- end
+ root date_detection: false do
+ field :id, type: 'long'
- field :acct, type: 'text', analyzer: 'content', value: ->(account) { account.username } do
- field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
- end
+ field :display_name, type: 'text', analyzer: 'content' do
+ field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
+ field :keyword, type: 'keyword'
+ end
- field :following_count, type: 'long', value: ->(account) { account.following_count.negative? ? 0 : account.following_count }
- field :followers_count, type: 'long', value: ->(account) { account.followers_count.negative? ? 0 : account.followers_count }
- field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
+ field :acct, type: 'text', analyzer: 'content', value: ->(account) { account.username } do
+ field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
+ field :keyword, type: 'keyword'
end
+
+ field :following_count, type: 'long', value: ->(account) { account.following_count.negative? ? 0 : account.following_count }
+ field :followers_count, type: 'long', value: ->(account) { account.followers_count.negative? ? 0 : account.followers_count }
+ field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
+
+ field :suspended, type: 'boolean', value: ->(account) { account.suspended? }
+ field :disabled, type: 'boolean', value: ->(account) { account.user_disabled? }
+ field :hidden, type: 'boolean', value: -> { false }
+
+ field :email, type: 'text', analyzer: 'exact', value: ->(account) { account.user_email }do
+ field :keyword, type: 'keyword'
+ end
+ field :created_at, type: 'date', value: ->(account) { account.created_at }
+
+ field :sms, type: 'text', analyzer: 'phone', search_analyzer: 'exact', value: ->(account) { account.user_sms }
end
end
diff -ru truth-old/opensource/app/chewy/statuses_index.rb truth-new/opensource/app/chewy/statuses_index.rb
--- truth-old/opensource/app/chewy/statuses_index.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/chewy/statuses_index.rb 2024-04-01 14:59:13
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class StatusesIndex < Chewy::Index
- settings index: { refresh_interval: '15m' }, number_of_shards: '12', analysis: {
+ settings index: { refresh_interval: '1m' }, number_of_shards: '12', analysis: {
filter: {
english_stop: {
type: 'stop',
@@ -31,22 +31,22 @@
},
}
- define_type ::Status.unscoped.kept.without_reblogs.includes(:status_stat) do
+ index_scope ::Status.unscoped.kept.without_reblogs.includes(:status_favourite, :status_reply, :status_reblog)
- root date_detection: false do
- field :id, type: 'long'
- field :account_id, type: 'long'
+ root date_detection: false do
+ field :id, type: 'long'
+ field :account_id, type: 'long'
- field :text, type: 'text', value: ->(status) {
- [status.spoiler_text, Formatter.instance.plaintext(status)].reject(&:blank?).join("\n\n")
- } do
- field :stemmed, type: 'text', analyzer: 'content'
- end
+ field :text, type: 'text', value: ->(status) {
+ [status.spoiler_text, Formatter.instance.plaintext(status)].reject(&:blank?).join("\n\n")
+ } do
+ field :stemmed, type: 'text', analyzer: 'content'
+ end
- field :activity, type: 'integer', value: ->(status) { (status.reblogs_count * 3) + status.favourites_count }
+ field :activity, type: 'integer', value: ->(status) { (status.reblogs_count * 3) + status.favourites_count }
- field :created_at, type: 'date'
- end
- end
+ field :created_at, type: 'date'
+ field :text_hash, type: 'keyword'
+ end
end
diff -ru truth-old/opensource/app/chewy/tags_index.rb truth-new/opensource/app/chewy/tags_index.rb
--- truth-old/opensource/app/chewy/tags_index.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/chewy/tags_index.rb 2024-04-01 14:59:13
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class TagsIndex < Chewy::Index
- settings index: { refresh_interval: '15m' }, number_of_shards: '12', analysis: {
+ settings index: { refresh_interval: '1m' }, number_of_shards: '12', analysis: {
analyzer: {
content: {
tokenizer: 'keyword',
@@ -23,17 +23,15 @@
},
}
- define_type ::Tag.listable, delete_if: ->(tag) {
- tag.destroyed? || !tag.listable?
- } do
- root date_detection: false do
- field :name, type: 'text', analyzer: 'content' do
- field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
- end
+ index_scope ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? }
- field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }
- field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day[:accounts].to_i } }
- field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }
+ root date_detection: false do
+ field :name, type: 'text', analyzer: 'content' do
+ field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content'
end
+
+ field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }
+ field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day[:accounts].to_i } }
+ field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }
end
end
diff -ru truth-old/opensource/app/controllers/accounts_controller.rb truth-new/opensource/app/controllers/accounts_controller.rb
--- truth-old/opensource/app/controllers/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/accounts_controller.rb 2024-04-01 14:59:13
@@ -28,7 +28,7 @@
return
end
- @pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
+ @pinned_statuses = cache_collection(@account.pinned_statuses.merge!(StatusPin.profile_pins), Status) if show_pinned_statuses?
@statuses = cached_filtered_status_page
@rss_url = rss_url
diff -ru truth-old/opensource/app/controllers/activitypub/collections_controller.rb truth-new/opensource/app/controllers/activitypub/collections_controller.rb
--- truth-old/opensource/app/controllers/activitypub/collections_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/activitypub/collections_controller.rb 2024-04-01 14:59:13
@@ -20,7 +20,7 @@
def set_items
case params[:id]
when 'featured'
- @items = for_signed_account { cache_collection(@account.pinned_statuses, Status) }
+ @items = for_signed_account { cache_collection(@account.pinned_statuses.merge!(StatusPin.profile_pins), Status) }
when 'tags'
@items = for_signed_account { @account.featured_tags }
when 'devices'
Only in truth-old/opensource/app/controllers/activitypub: inboxes_controller.rb
Only in truth-old/opensource/app/controllers/activitypub: outboxes_controller.rb
diff -ru truth-old/opensource/app/controllers/admin/accounts_controller.rb truth-new/opensource/app/controllers/admin/accounts_controller.rb
--- truth-old/opensource/app/controllers/admin/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/admin/accounts_controller.rb 2024-04-01 14:59:13
@@ -43,13 +43,20 @@
def reject
authorize @account.user, :reject?
- DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
+ DeleteAccountService.new.call(
+ @account,
+ @current_account.id,
+ deletion_type: 'admin_reject',
+ reserve_email: false,
+ reserve_username: false,
+ skip_activitypub: true,
+ )
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
end
def destroy
authorize @account, :destroy?
- Admin::AccountDeletionWorker.perform_async(@account.id)
+ Admin::AccountDeletionWorker.perform_async(@account.id, @current_account.id)
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct)
end
Only in truth-old/opensource/app/controllers/admin: trending_truths_controller.rb
diff -ru truth-old/opensource/app/controllers/api/base_controller.rb truth-new/opensource/app/controllers/api/base_controller.rb
--- truth-old/opensource/app/controllers/api/base_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/base_controller.rb 2024-04-12 09:09:08
@@ -3,8 +3,13 @@
class Api::BaseController < ApplicationController
DEFAULT_STATUSES_LIMIT = 20
DEFAULT_ACCOUNTS_LIMIT = 40
+ VERIFICATION_INTERVAL = 1.hour.ago.freeze
+ RATE_LIMIT_EXPIRE_AFTER = 7.days.seconds
include RateLimitHeaders
+ include Redisable
+ include AppAttestable
+ include Clientable
skip_before_action :store_current_location
skip_before_action :require_functional!, unless: :whitelist_mode?
@@ -17,76 +22,111 @@
skip_around_action :set_locale
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
- errror_message = e.to_s
+ error_message = e.to_s
if params[:controller] == 'api/v1/admin/accounts' && params[:action] == 'create'
filters = Rails.application.config.filter_parameters
f = ActiveSupport::ParameterFilter.new filters
filtered_params = f.filter params
- Rails.logger.info("Unsuccessful registration: #{errror_message}. params: #{filtered_params}")
+ Rails.logger.info("Unsuccessful registration: #{error_message}. params: #{filtered_params}")
end
- response = { error: errror_message }
+ error = error_message
+ additional_fields = {}
- # Output an errorCode key w/ value instead of shoving it into one giant string in the error key
- # Usage: object.errors.add(:errorCode, 'machine_readable_text')
- if (errror_message.include?('Errorcode'))
- message = errror_message.split(', Errorcode ')
- formatted_readable_message = message[0].sub('Validation failed: ', '')
- response = { error: formatted_readable_message, errorCode: message[1] }
+ # Output an error_code key w/ value instead of shoving it into one giant string in the error key
+ # Usage: object.errors.add(:error_code, 'machine_readable_text')
+ if error_message.include?('Error code')
+ message = error_message.split(', Error code ')
+ error = message[0].sub(I18n.t('activerecord.errors.messages.record_invalid').split(': ')[0], '').sub(': ', '') # there's not an easy way to remove the 'Validation failed' text in regards to localization
+ code = message[1]
+ # Need this for backwards compatibility
+ if message[1] == 'followLimitReached'
+ code = message[1].underscore
+ additional_fields[:errorCode] = message[1]
+ elsif request.path == validate_api_v1_groups_path
+ additional_fields[:message] = message[1]
+ else
+ additional_fields
+ end
end
- render json: response, status: 422
+ render_error(422, error, code, error, additional_fields)
end
- rescue_from ActiveRecord::RecordNotUnique do
- render json: { error: 'Duplicate record' }, status: 422
+ rescue_from ActiveRecord::RecordNotUnique do |e|
+ Rails.logger.error "RecordNotUnique: #{e}, user: #{current_user.id}, user_agent: #{request.user_agent}"
+ message = I18n.t('errors.api.duplicate')
+ render_error(422, message, message, message)
end
rescue_from ActiveRecord::RecordNotFound do
- render json: { error: 'Record not found' }, status: 404
+ message = I18n.t('errors.api.404')
+ render_error(404, message, nil, message)
end
rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
- render json: { error: 'Remote data could not be fetched' }, status: 503
+ render json: { error: I18n.t('errors.api.data_fetch') }, status: 503
end
rescue_from OpenSSL::SSL::SSLError do
- render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
+ render json: { error: I18n.t('errors.api.ssl') }, status: 503
end
rescue_from Mastodon::NotPermittedError do
- render json: { error: 'This action is not allowed' }, status: 403
+ message = I18n.t('errors.api.403')
+ render_error(403, message, nil, message)
end
+ rescue_from Mastodon::UnprocessableAssertion do |e|
+ alert(e.message) unless e.message == e.class.to_s
+ render json: { error: 'Unable to verify assertion' }, status: 422
+ end
+
+ rescue_from Mastodon::AttestationError do
+ render json: { error: 'Unable to verify attestation' }, status: 400
+ end
+
rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do |e|
- Rails.logger.info("Network error: #{e.message} #{request.remote_ip} #{request.request_method} #{request.fullpath} #{current_user.id}") if e.class == Seahorse::Client::NetworkingError
- render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
+ Rails.logger.info("Network error: #{e.message} #{request.remote_ip} #{request.request_method} #{request.fullpath} #{current_user.id}") if e.instance_of?(Seahorse::Client::NetworkingError)
+ render json: { error: I18n.t('errors.api.503') }, status: 503
end
rescue_from Mastodon::RateLimitExceededError do |e|
Rails.logger.info("#{e.message} #{request.remote_ip} #{request.request_method} #{request.fullpath} #{current_user.id}")
+ track_rate_limited_user
render json: { error: I18n.t('errors.429') }, status: 429
end
+ rescue_from Mastodon::HostileRateLimitExceededError do |e|
+ Rails.logger.info("#{e.message} #{request.remote_ip} #{request.request_method} #{request.fullpath} #{current_user.id}")
+ track_hostile_rate_limited_user
+ render json: {}, status: 200
+ end
+
rescue_from ActionController::ParameterMissing do |e|
render json: { error: e.to_s }, status: 400
end
+ rescue_from WebAuthn::Error do |e|
+ render json: { error: e.to_s }, status: 400
+ end
+
def doorkeeper_unauthorized_render_options(error: nil)
- { json: { error: (error.try(:description) || 'Not authorized') } }
+ { json: { error: (error.try(:description) || I18n.t('errors.api.unauthorized')) } }
end
def doorkeeper_forbidden_render_options(*)
- { json: { error: 'This action is outside the authorized scopes' } }
+ { json: { error: I18n.t('errors.api.outside_scopes') } }
end
protected
- def set_pagination_headers(next_path = nil, prev_path = nil)
+ def set_pagination_headers(next_path = nil, prev_path = nil, offset = nil)
links = []
links << [next_path, [%w(rel next)]] if next_path
links << [prev_path, [%w(rel prev)]] if prev_path
+ links << [offset, [%w(rel self)]] if offset
response.headers['Link'] = LinkHeader.new(links) unless links.empty?
end
@@ -105,7 +145,7 @@
end
def current_resource_owner
- @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
+ @current_user ||= User.with_reverification.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end
def current_user
@@ -115,18 +155,25 @@
end
def require_authenticated_user!
- render json: { error: 'This method requires an authenticated user' }, status: 401 unless current_user
+ render json: { error: I18n.t('errors.api.401') }, status: 401 unless current_user
end
- def require_user!(requires_approval: true)
+ def require_user!(requires_approval: true, skip_sms_reverification: false)
if !current_user
- render json: { error: 'This method requires an authenticated user' }, status: 422
+ alert('Current user is missing') if assert_request?
+ render json: { error: I18n.t('errors.api.401') }, status: 422
elsif !current_user.confirmed?
- render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
+ alert("Current user: #{current_user.id} is not confirmed") if assert_request?
+ render json: { error: I18n.t('errors.api.missing_email') }, status: 403
elsif requires_approval && !current_user.approved?
- render json: { error: 'Your login is currently pending approval' }, status: 403
+ alert("Current user: #{current_user.id} is not approved") if assert_request?
+ render json: { error: I18n.t('errors.api.login_pending') }, status: 403
elsif requires_approval && !current_user.functional?
- render json: { error: 'Your login is currently disabled' }, status: 403
+ alert("Current user: #{current_user.id} is not functional") if assert_request?
+ render json: { error: I18n.t('errors.api.login_disabled') }, status: 403
+ elsif !skip_sms_reverification && sms_reverification_required?
+ alert("Current user: #{current_user.id} is waiting sms verification") if assert_request?
+ render json: { error: I18n.t('errors.api.sms_reverification_pending') }, status: 403
else
update_user_sign_in
end
@@ -136,6 +183,20 @@
render json: {}, status: 200
end
+ # rubocop:disable Metrics/ParameterLists
+ def render_error(status, error_message = nil, code = nil, error = nil, additional_fields = {})
+ default_code = Rack::Utils::HTTP_STATUS_CODES[status]
+ response = {
+ error_message: error_message || default_code,
+ error_code: format_code(code || default_code),
+ error: error,
+ **additional_fields,
+ }.compact
+
+ render json: response, status: status
+ end
+ # rubocop:enable Metrics/ParameterLists
+
def authorize_if_got_token!(*scopes)
doorkeeper_authorize!(*scopes) if doorkeeper_token
end
@@ -146,5 +207,51 @@
def disallow_unauthenticated_api_access?
authorized_fetch_mode?
+ end
+
+ def track_rate_limited_user
+ redis_key = "rate_limit:#{DateTime.current.to_date}"
+ redis_element_key = "#{current_user.id}-#{request.remote_ip}"
+ Redis.current.zincrby(redis_key, 1, redis_element_key)
+ Redis.current.expire(redis_key, RATE_LIMIT_EXPIRE_AFTER)
+ end
+
+ def track_hostile_rate_limited_user
+ redis_key = "hostile_rate_limit:#{DateTime.current.to_date}"
+ redis_element_key = "#{current_user.id}-#{request.remote_ip}"
+ Redis.current.zincrby(redis_key, 1, redis_element_key)
+ Redis.current.expire(redis_key, RATE_LIMIT_EXPIRE_AFTER)
+ end
+
+ def raw_request_body
+ @request_body ||= JSON.parse(request.raw_post)
+ end
+
+ def assert_request?
+ request.path == '/api/v1/truth/ios_device_check/assert'
+ end
+
+ def sms_reverification_required?
+ return false unless current_user&.user_sms_reverification_required&.user_id
+
+ return false if app_attest_path?
+
+ if android_client?
+ return false if request.headers['x-tru-assertion']
+ credential = doorkeeper_token.integrity_credentials.order(last_verified_at: :desc).first
+ unverified_credential = credential.present? ? false : true
+ elsif ios_client?
+ credential = doorkeeper_token.token_webauthn_credentials.order(last_verified_at: :desc).first
+ unverified_credential = credential.present? ? false : true
+ else
+ unverified_credential = true
+ end
+
+ blocked_methods = %w(POST PUT PATCH)
+ !!(unverified_credential && blocked_methods.include?(request.request_method))
+ end
+
+ def app_attest_path?
+ request.path.start_with?('/api/v1/truth/ios_device_check')
end
end
Only in truth-new/opensource/app/controllers/api: docs_controller.rb
Only in truth-new/opensource/app/controllers/api: mock
diff -ru truth-old/opensource/app/controllers/api/oembed_controller.rb truth-new/opensource/app/controllers/api/oembed_controller.rb
--- truth-old/opensource/app/controllers/api/oembed_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/oembed_controller.rb 2024-04-01 14:59:13
@@ -7,7 +7,7 @@
before_action :require_public_status!
def show
- render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default
+ render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default, has_video: has_video
end
private
@@ -17,18 +17,28 @@
end
def require_public_status!
- not_found if @status.hidden?
+ not_found if !distributable?
end
+ def distributable?
+ @status.public_visibility? || @status.unlisted_visibility? || @status.group&.everyone?
+ end
+
def status_finder
StatusFinder.new(params[:url])
end
def maxwidth_or_default
- (params[:maxwidth].presence || 400).to_i
+ (params[:maxwidth].presence || 600).to_i
end
def maxheight_or_default
params[:maxheight].present? ? params[:maxheight].to_i : nil
+ end
+
+ def has_video
+ if @status.with_media?
+ @status.media_attachments.first.video?
+ end
end
end
diff -ru truth-old/opensource/app/controllers/api/pleroma/accounts_controller.rb truth-new/opensource/app/controllers/api/pleroma/accounts_controller.rb
--- truth-old/opensource/app/controllers/api/pleroma/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/pleroma/accounts_controller.rb 2024-04-01 14:59:13
@@ -1,5 +1,7 @@
# frozen_string_literal: true
class Api::Pleroma::AccountsController < Api::BaseController
+ before_action -> { doorkeeper_authorize! :read }, only: [:mfa, :setup_totp, :backup_codes]
+ before_action -> { doorkeeper_authorize! :write }, only: [:confirm_totp, :delete_totp]
before_action :require_user!
before_action :prepare_two_factor, only: [:setup_totp]
before_action :validate_password, only: [:confirm_totp, :delete_totp]
diff -ru truth-old/opensource/app/controllers/api/pleroma/user_settings_controller.rb truth-new/opensource/app/controllers/api/pleroma/user_settings_controller.rb
--- truth-old/opensource/app/controllers/api/pleroma/user_settings_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/pleroma/user_settings_controller.rb 2024-04-01 14:59:13
@@ -1,15 +1,34 @@
# frozen_string_literal: true
class Api::Pleroma::UserSettingsController < Api::BaseController
+ before_action -> { doorkeeper_authorize! :write }
before_action :require_user!
before_action :validate_password
before_action :validate_email, only: :change_email
before_action :set_new_email, only: :change_email
+ around_action :set_locale, only: :change_password
def change_password
if current_user.reset_password(resource_params[:new_password], resource_params[:new_password_confirmation])
+ OauthAccessToken.where.not(token: doorkeeper_token.token).where(resource_owner_id: current_user.id).update_all(revoked_at: Time.now.utc)
+
render json: { status: :success }
else
- render json: { error: 'Password and password confirmation do not match.' }, status: 400
+ errors = current_user.errors.to_hash
+ password_invalid = errors[:password]&.pop
+ default_error = I18n.t('users.password_mismatch', locale: :en)
+ message, message_with_locale, code =
+ if password_invalid.present?
+ error = errors[:base]&.pop || default_error
+ [error, password_invalid, 'PASSWORD_INVALID']
+ else
+ [default_error, I18n.t('users.password_mismatch'), 'PASSWORD_MISMATCH']
+ end
+
+ render json: {
+ error: message,
+ error_code: code,
+ error_message: message_with_locale,
+ }, status: 400
end
end
@@ -40,7 +59,13 @@
def destroy_account!
current_account.suspend!(origin: :local)
- AccountDeletionWorker.perform_async(current_user.account_id)
+ account_id = current_user.account_id
+ AccountDeletionWorker.perform_async(
+ account_id,
+ account_id,
+ deletion_type: 'self_deletion',
+ skip_activitypub: true,
+ )
sign_out
end
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/credentials_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/credentials_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/credentials_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/credentials_controller.rb 2024-04-12 16:26:23
@@ -2,17 +2,23 @@
class Api::V1::Accounts::CredentialsController < Api::BaseController
include JwtConcern
+ include Clientable
- before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:update]
+ TV_REQUIRED_IOS_VERSION = ENV.fetch('TV_REQUIRED_IOS_VERSION', 0).to_i
+ TV_REQUIRED_ANDROID_VERSION = ENV.fetch('TV_REQUIRED_ANDROID_VERSION', 0).to_i
+
+ before_action -> { doorkeeper_authorize! :read, :'read:accounts', :ads }, except: [:update]
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
skip_before_action :require_functional!, only: [:show]
before_action :require_user!, only: [:update, :chat_token]
before_action :set_account, only: [:update, :chat_token, :show]
before_action :verify_credentials_require_user!, only: [:show]
+ before_action :enable_feature_flag, only: [:show]
+ before_action :create_tv_user, only: [:show]
def show
- render json: @account, serializer: REST::CredentialAccountSerializer
+ render json: @account, serializer: REST::CredentialAccountSerializer, access_token: doorkeeper_token, android_client: android_client?, tv_account_lookup: true
end
def chat_token
@@ -29,6 +35,7 @@
def update
UpdateAccountService.new.call(@account, account_params, raise_error: true)
UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params
+ current_user.update!(unauth_visibility_params) if unauth_visibility_params
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
render json: @account, serializer: REST::CredentialAccountSerializer
end
@@ -36,9 +43,36 @@
private
def account_params
- params.permit(:display_name, :location, :website, :note, :avatar, :header, :locked, :discoverable, pleroma_settings_store: {}, fields_attributes: [:name, :value])
+ # Pleroma compatibility
+ params[:accepting_messages] = params[:accepting_messages] || accepts_chats_messages || !!@account.accepting_messages
+ params[:feeds_onboarded] = truthy_param?(:feeds_onboarded) if params[:feeds_onboarded]
+ params[:tv_onboarded] = truthy_param?(:tv_onboarded) if params[:tv_onboarded]
+ params[:show_nonmember_group_statuses] = truthy_param?(:show_nonmember_group_statuses) if params[:show_nonmember_group_statuses]
+ params[:receive_only_follow_mentions] = truthy_param?(:receive_only_follow_mentions) if params[:receive_only_follow_mentions]
+
+ params.permit(:display_name,
+ :location,
+ :website,
+ :note,
+ :avatar,
+ :header,
+ :locked,
+ :discoverable,
+ :accepting_messages,
+ :chats_onboarded,
+ :feeds_onboarded,
+ :tv_onboarded,
+ :show_nonmember_group_statuses,
+ :receive_only_follow_mentions,
+ pleroma_settings_store: {},
+ fields_attributes: [:name, :value]
+ )
end
+ def accepts_chats_messages
+ params[:accepts_chat_messages].to_s.empty? ? false : params[:accepts_chat_messages].to_s
+ end
+
def set_account
@account = current_account
end
@@ -55,6 +89,10 @@
}
end
+ def unauth_visibility_params
+ params.permit(:unauth_visibility)
+ end
+
def verify_credentials_require_user!
if !current_user
render json: { error: 'This method requires an authenticated user' }, status: 422
@@ -67,5 +105,38 @@
else
update_user_sign_in
end
+ end
+
+ def create_tv_user
+ return if invalid_ios_version? && !current_account.tv_enabled?
+
+ session_id = TvDeviceSession.find_by(oauth_access_token_id: doorkeeper_token.id)&.tv_session_id
+ tv_profile_id = TvAccount.find_by(account_id: current_account.id)&.p_profile_id
+
+ return if session_id.present? && tv_profile_id.present?
+
+ if tv_profile_id.nil?
+ TvAccountsCreateWorker.perform_async(current_account.id, doorkeeper_token.id)
+ else
+ TvAccountsLoginWorker.perform_async(current_account.id, doorkeeper_token.id)
+ end
+ end
+
+ def enable_feature_flag
+ return unless required_android_version
+ ::Configuration::AccountEnabledFeature.upsert(
+ account_id: current_account.id,
+ feature_flag_id: 1,
+ )
+ end
+
+ def invalid_ios_version?
+ ios_version = request&.user_agent&.strip&.match(/^TruthSocial\/(\d+) .+/) || []
+ ios_version[1].nil? || TV_REQUIRED_IOS_VERSION.zero? || ios_version[1].to_i < TV_REQUIRED_IOS_VERSION
+ end
+
+ def required_android_version
+ android_version = request&.user_agent&.strip&.match(/^TruthSocialAndroid\/okhttp\/.+\/(\d+)/) || []
+ !android_version[1].nil? && !TV_REQUIRED_ANDROID_VERSION.zero? && android_version[1].to_i == TV_REQUIRED_ANDROID_VERSION
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/follower_accounts_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/follower_accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/follower_accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/follower_accounts_controller.rb 2024-04-01 14:59:13
@@ -14,6 +14,7 @@
def set_account
@account = Account.find(params[:account_id])
+ @pagination_enabled = (@account == current_account)
end
def load_accounts
@@ -29,30 +30,38 @@
end
def default_accounts
- Account.includes(:active_relationships, :account_stat).references(:active_relationships)
+ Account.includes(:active_relationships, :account_follower, :account_following, :account_status).references(:active_relationships)
end
def paginated_follows
- Follow.where(target_account: @account).paginate_by_min_id(
- limit_param(DEFAULT_ACCOUNTS_LIMIT),
- params[:min_id],
- params[:max_id]
- )
+ if @pagination_enabled
+ Follow.where(target_account: @account).paginate_by_max_id(
+ limit_param(DEFAULT_ACCOUNTS_LIMIT),
+ params[:max_id],
+ params[:since_id]
+ )
+ else
+ Follow.where(target_account: @account).paginate_by_min_id(
+ limit_param(DEFAULT_ACCOUNTS_LIMIT),
+ nil,
+ nil
+ )
+ end
end
def insert_pagination_headers
- set_pagination_headers(next_path, prev_path)
+ set_pagination_headers(next_path, prev_path) if @pagination_enabled
end
def next_path
if records_continue?
- api_v1_account_followers_url pagination_params(min_id: pagination_max_id)
+ api_v1_account_followers_url pagination_params(max_id: pagination_max_id)
end
end
def prev_path
unless @accounts.empty?
- api_v1_account_followers_url pagination_params(max_id: pagination_min_id)
+ api_v1_account_followers_url pagination_params(since_id: pagination_since_id)
end
end
@@ -60,7 +69,7 @@
@accounts.last.active_relationships.first.id
end
- def pagination_min_id
+ def pagination_since_id
@accounts.first.active_relationships.first.id
end
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/following_accounts_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/following_accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/following_accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/following_accounts_controller.rb 2024-04-01 14:59:13
@@ -29,7 +29,7 @@
end
def default_accounts
- Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
+ Account.includes(:passive_relationships, :account_follower, :account_following, :account_status).references(:passive_relationships)
end
def paginated_follows
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/lookup_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/lookup_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/lookup_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/lookup_controller.rb 2024-04-01 14:59:13
@@ -6,7 +6,7 @@
before_action :require_authenticated_user!, unless: :allowed_public_access?
def show
- render json: @account, serializer: REST::AccountSerializer
+ render json: @account, serializer: REST::AccountSerializer, tv_account_lookup: true
end
private
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/relationships_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/relationships_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/relationships_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/relationships_controller.rb 2024-04-05 09:07:34
@@ -6,16 +6,19 @@
def index
accounts = Account.without_suspended.where(id: account_ids).select('id')
- # .where doesn't guarantee that our results are in the same order
- # we requested them, so return the "right" order to the requestor.
- @accounts = accounts.index_by(&:id).values_at(*account_ids).compact
- render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
+ @accounts = accounts.index_by(&:id).values_at(*account_ids).compact # order results
+ render json: Panko::ArraySerializer.new(
+ @accounts, each_serializer: REST::V2::RelationshipSerializer,
+ context: {
+ relationships: relationships,
+ }
+ ).to_json
end
private
def relationships
- AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
+ V2::AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
end
def account_ids
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/search_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/search_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/search_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/search_controller.rb 2024-04-01 14:59:13
@@ -3,10 +3,11 @@
class Api::V1::Accounts::SearchController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
before_action :require_user!
+ after_action :insert_pagination_headers, unless: -> { @accounts.empty? }, only: :show
def show
@accounts = account_search
- render json: @accounts, each_serializer: REST::AccountSerializer
+ render json: @accounts, each_serializer: REST::AccountSerializer, tv_account_lookup: true
end
private
@@ -18,7 +19,26 @@
limit: limit_param(DEFAULT_ACCOUNTS_LIMIT),
resolve: truthy_param?(:resolve),
following: truthy_param?(:following),
+ followers: truthy_param?(:followers),
offset: params[:offset]
)
+ end
+
+ def insert_pagination_headers
+ set_pagination_headers(next_path)
+ end
+
+ def next_path
+ if records_continue?
+ api_v1_accounts_search_url pagination_params(offset: @accounts.size + params[:offset].to_i)
+ end
+ end
+
+ def records_continue?
+ @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ end
+
+ def pagination_params(core_params)
+ params.slice(:limit, :followers, :q).permit(:limit, :followers, :q).merge(core_params)
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/accounts/statuses_controller.rb truth-new/opensource/app/controllers/api/v1/accounts/statuses_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts/statuses_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts/statuses_controller.rb 2024-04-01 14:59:13
@@ -8,7 +8,11 @@
after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }
def index
- @statuses = load_statuses
+ @statuses = load_statuses
+ if (ad_indexes = ENV.fetch('X_TRUTH_AD_INDEXES', nil))
+ response.headers['x-truth-ad-indexes'] = ad_indexes
+ end
+
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
end
@@ -49,7 +53,7 @@
def pinned_scope
return Status.none if @account.blocking?(current_account)
- @account.pinned_statuses
+ @account.pinned_statuses.merge!(StatusPin.profile_pins)
end
def no_replies_scope
diff -ru truth-old/opensource/app/controllers/api/v1/accounts_controller.rb truth-new/opensource/app/controllers/api/v1/accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/accounts_controller.rb 2024-04-01 14:59:13
@@ -21,7 +21,7 @@
end
def follow
- follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, with_rate_limit: false)
+ follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, with_rate_limit: true)
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify? } }, requested_map: { @account.id => false } }
export_prometheus_metric(:follows)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
@@ -33,7 +33,7 @@
end
def mute
- MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration]&.to_i || 0))
+ MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: params.fetch(:duration, 0).to_i)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end
@@ -80,7 +80,6 @@
end
def export_prometheus_metric(metric_to_track)
- Prometheus::ApplicationExporter::increment(metric_to_track)
+ Prometheus::ApplicationExporter.increment(metric_to_track)
end
-
end
diff -ru truth-old/opensource/app/controllers/api/v1/admin/account_actions_controller.rb truth-new/opensource/app/controllers/api/v1/admin/account_actions_controller.rb
--- truth-old/opensource/app/controllers/api/v1/admin/account_actions_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/admin/account_actions_controller.rb 2024-04-01 14:59:13
@@ -11,7 +11,7 @@
account_action.current_account = current_account
account_action.save!
- render_empty
+ render json: @account, serializer: REST::Admin::AccountSerializer
end
private
@@ -27,7 +27,8 @@
:warning_preset_id,
:text,
:send_email_notification,
- :duration
+ :duration,
+ :feature_name
)
end
end
Only in truth-new/opensource/app/controllers/api/v1/admin/accounts: statuses_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin/accounts: webauthn_credentials_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/admin/accounts_controller.rb truth-new/opensource/app/controllers/api/v1/admin/accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/admin/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/admin/accounts_controller.rb 2024-04-05 09:07:34
@@ -6,14 +6,21 @@
LIMIT = 100
+ before_action :set_log_level
+
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
before_action :require_staff!
before_action :set_accounts, only: :index
before_action :set_account, except: [:index, :create, :bulk_approve]
+ before_action :set_policy, only: :create
before_action :require_local_account!, only: [:enable, :approve, :reject]
+ before_action :set_geo, only: [:create]
+ before_action :set_registrations, only: [:create], if: -> { params[:token].present? }
after_action :insert_pagination_headers, only: :index
+ after_action :registration_cleanup, only: :create, if: -> { @user.persisted? && @registration }
+ after_action :revert_log_level
FILTER_PARAMS = %i(
local
@@ -33,11 +40,25 @@
sms
).freeze
+ GEO_PARAMS = %i(
+ country_name
+ country_code
+ city_name
+ region_code
+ region_name
+ ).freeze
+
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
def index
authorize :account, :index?
- render json: @accounts, each_serializer: REST::Admin::AccountSerializer
+ render json: Panko::ArraySerializer.new(
+ @accounts,
+ each_serializer: REST::V2::Admin::AccountSerializer,
+ context: {
+ advertisers: Account.recent_advertisers(@accounts.pluck(:id)),
+ }
+ ).to_json
end
def show
@@ -47,7 +68,7 @@
def update
update_hash = update_params.to_h
- if (!@account.user.not_ready_for_approval? && !@account.user.ready_by_csv_import?)
+ if !@account.user.not_ready_for_approval? && !@account.user.ready_by_csv_import?
update_hash[:approved] = true
export_prometheus_metric(:approves)
end
@@ -61,12 +82,15 @@
end
def create
+ Rails.logger.info("Sign-up logs: attempting to register: #{params[:email]}")
+
account = Account.new(
username: params[:username],
- discoverable: params[:role] != 'moderator'
+ discoverable: params[:role] != 'moderator',
+ feeds_onboarded: true
)
- user = User.new(
+ @user = User.new(
email: params[:email],
password: params[:password],
sms: params[:sms],
@@ -75,19 +99,26 @@
approved: ['true', true].include?(params[:approved]),
moderator: params[:role] == 'moderator',
confirmed_at: params[:confirmed] ? Time.now.utc : nil,
- bypass_invite_request_check: true
+ bypass_invite_request_check: true,
+ policy: @policy,
+ sign_up_city_id: @city,
+ sign_up_country_id: @country,
+ sign_up_ip: params[:sign_up_ip]
)
- user.account = account
+ @user.account = account
account.verify! if ['true', true].include?(params[:verified])
- user.set_waitlist_position unless user.approved
+ @user.set_waitlist_position unless @user.approved
- if user.save
- send_registration_email(user)
+ if @user.save
+ send_registration_email
export_prometheus_metric(:registrations)
- render json: user.account, serializer: REST::Admin::AccountCreateSerializer
+ dispatch_rmq_event(account, params)
+ log_successful_attempt
+ render json: @user.account, serializer: REST::Admin::AccountCreateSerializer
else
- user_errors = user.errors.to_h
+ user_errors = @user.errors.to_h
+ log_failed_attempt(user_errors)
render json: { errors: user_errors }, status: 422
end
end
@@ -126,13 +157,20 @@
def reject
authorize @account.user, :reject?
- DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
+ DeleteAccountService.new.call(
+ @account,
+ @current_account.id,
+ deletion_type: 'api_admin_reject',
+ reserve_email: false,
+ reserve_username: false,
+ skip_activitypub: true,
+ )
render json: @account, serializer: REST::Admin::AccountSerializer
end
def destroy
authorize @account, :destroy?
- Admin::AccountDeletionWorker.perform_async(@account.id)
+ Admin::AccountDeletionWorker.perform_async(@account.id, @current_account.id)
render json: @account, serializer: REST::Admin::AccountSerializer
end
@@ -231,14 +269,87 @@
end
def export_prometheus_metric(metric_type)
- Prometheus::ApplicationExporter::increment(metric_type)
+ Prometheus::ApplicationExporter.increment(metric_type)
end
- def send_registration_email(user)
- if user.approved?
- NotificationMailer.user_approved(user.account).deliver_later
+ def send_registration_email
+ if @user.approved?
+ NotificationMailer.user_approved(@user.account).deliver_later
else
- UserMailer.waitlisted(user).deliver_later
+ UserMailer.waitlisted(@user).deliver_later
end
+ end
+
+ def set_policy
+ @policy = Policy.last
+ end
+
+ def geo_params
+ params.permit(*GEO_PARAMS)
+ end
+
+ def set_geo
+ geo = GeoService.new(
+ city_name: geo_params[:city_name],
+ country_code: geo_params[:country_code],
+ country_name: geo_params[:country_name],
+ region_name: geo_params[:region_name],
+ region_code: geo_params[:region_code]
+ )
+
+ @city = geo.city
+ @country = geo.country
+ end
+
+ def set_log_level
+ @current_log_level = Rails.logger.level
+ Rails.logger.level = :debug
+ end
+
+ def revert_log_level
+ Rails.logger.level = @current_log_level
+ end
+
+ def set_registrations
+ @registration = Registration.find_by(token: params[:token])
+ if @registration&.ios_device?
+ @registration_credential = @registration.registration_webauthn_credential
+ @credential = @registration_credential&.webauthn_credential
+ credential_error = 'Webauthn Credential is already associated with an account'
+ render json: { errors: credential_error }, status: 422 and return if @credential&.user.present?
+ end
+ end
+
+ def registration_cleanup
+ if @registration.ios_device?
+ @credential&.update!(user: @user) # Do we want to fail loudly?
+ else
+ user_params = { user_id: @user.id }
+ verification = DeviceVerification.find_by("details ->> 'registration_token' = '#{ActiveRecord::Base.sanitize_sql(@registration.token)}'")
+ details = verification.details
+ new_details = details.merge(user_params)
+ verification.update(details: new_details)
+ end
+
+ registration_otc = @registration.registration_one_time_challenge
+ otc = registration_otc.one_time_challenge
+ otc.destroy # This will also cascade delete the RegistrationOneTimeChallenge record.
+ @registration.destroy
+ end
+
+ def dispatch_rmq_event(account, params)
+ EventProvider::EventProvider.new('account.created', AccountCreatedEvent, account, params).call
+ end
+
+ def log_failed_attempt(errors)
+ filters = Rails.application.config.filter_parameters
+ f = ActiveSupport::ParameterFilter.new filters
+ filtered_params = f.filter params
+
+ Rails.logger.info("Sign-up logs: unsuccessful registration: #{errors}. params: #{filtered_params}")
+ end
+
+ def log_successful_attempt
+ Rails.logger.info("Sign-up logs: successful registration: #{params[:email]}")
end
end
Only in truth-new/opensource/app/controllers/api/v1/admin: bulk_account_actions_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: chat_messages_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: groups
Only in truth-new/opensource/app/controllers/api/v1/admin: groups_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: links_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: policies_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: registrations_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/admin/statuses_controller.rb truth-new/opensource/app/controllers/api/v1/admin/statuses_controller.rb
--- truth-old/opensource/app/controllers/api/v1/admin/statuses_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/admin/statuses_controller.rb 2024-04-12 16:08:26
@@ -4,8 +4,11 @@
include Authorization
include AccountableConcern
+ before_action :set_log_level
before_action -> { doorkeeper_authorize! :'admin:write' }
+ before_action :require_staff!
before_action -> { set_status }, except: :index
+ after_action :revert_log_level
def index
@statuses = Status.with_discarded.where(id: params[:ids])
@@ -42,16 +45,27 @@
@status.reblogs.update_all(deleted_at: Time.current, deleted_by_id: resource_params[:moderator_id])
@status.update!(deleted_at: Time.current, deleted_by_id: resource_params[:moderator_id])
RemovalWorker.perform_async(@status.id, redraft: true, notify_user: resource_params[:notify_user], immediate: false)
- @status.account.statuses_count = @status.account.statuses_count - 1
- @status.account.save
invalidate_cache
render json: @status, serializer: REST::Admin::StatusSerializer, source_requested: true
end
+ def privatize
+ @status.privatize(resource_params[:moderator_id], resource_params[:notify_user])
+ invalidate_cache
+ render json: @status, serializer: REST::Admin::StatusSerializer, source_requested: true
+ end
+
+ def publicize
+ @status.publicize
+ invalidate_cache
+ render json: @status, serializer: REST::Admin::StatusSerializer, source_requested: true
+ end
+
private
def set_status
@status = Status.with_discarded.find(params[:id] || params[:status_id])
+ @status.performed_by_admin = true
end
def resource_params
@@ -63,4 +77,15 @@
InvalidateSecondaryCacheService.new.call("InvalidateStatusCacheWorker", @status.id)
end
+ def set_log_level
+ return unless request.request_method == 'POST'
+ Rails.logger.info("Admin::StatusesController logs: #{params.inspect}")
+ @current_log_level = Rails.logger.level
+ Rails.logger.level = :debug
+ end
+
+ def revert_log_level
+ return unless request.request_method == 'POST'
+ Rails.logger.level = @current_log_level
+ end
end
Only in truth-new/opensource/app/controllers/api/v1/admin: tags_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: trending_groups_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: trending_statuses
diff -ru truth-old/opensource/app/controllers/api/v1/admin/trending_statuses_controller.rb truth-new/opensource/app/controllers/api/v1/admin/trending_statuses_controller.rb
--- truth-old/opensource/app/controllers/api/v1/admin/trending_statuses_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/admin/trending_statuses_controller.rb 2024-04-01 14:59:13
@@ -1,21 +1,23 @@
# frozen_string_literal: true
class Api::V1::Admin::TrendingStatusesController < Api::BaseController
+ TRENDING_STATUS_LIMIT = 10
+
before_action -> { doorkeeper_authorize! :'admin:write' }
before_action :require_staff!
before_action :set_statuses, only: :index
- before_action :set_status, only: [:update, :destroy]
+ before_action :set_status, only: [:include, :exclude]
after_action :set_pagination_headers, only: :index
def index
render json: @statuses, each_serializer: REST::Admin::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
end
- def update
- mark_trending
+ def include
+ render json: TrendingStatusExcludedStatus.destroy(@status.id)
end
- def destroy
- remove_from_trending
+ def exclude
+ render json: TrendingStatusExcludedStatus.create(status_id: @status.id)
end
private
@@ -24,52 +26,14 @@
@status = Status.find(params[:id])
end
- def mark_trending
- trending = Trending.where(status: @status).first_or_initialize
- trending.user = current_user if trending.new_record?
- trending.save!
- end
-
- def remove_from_trending
- trending = Trending.find_by(status_id: @status.id)
- trending.delete if trending.present?
- end
-
def set_statuses
- @statuses = if params[:trending]
- Trending.includes(:status).all.flat_map(&:status)
- else
- fetch_recent_statuses
- end
+ @statuses = Status.trending_statuses.page(params[:page]).per(TRENDING_STATUS_LIMIT)
end
- def fetch_recent_statuses
- Rails.cache.fetch("admin:trending_statuses:#{params[:page]}", expires_in: 10.minutes) do
- Status
- .where(created_at: 1.day.ago.beginning_of_day..Time.now)
- .joins(:status_stat)
- .includes(:preview_cards, :status_stat, account: :account_stat)
- .order("status_stats.favourites_count desc")
- .reorder('')
- .page(params[:page])
- .per(10)
- .to_a
- end
- end
-
def set_pagination_headers
- response.headers["x-page-size"] = 10
- response.headers["x-page"] = params[:page] || 1
- response.headers["x-total"] = if @statuses.is_a?(Array)
- @statuses.size
- else
- @statuses.total_count
- end
-
- response.headers["x-total-pages"] = if @statuses.is_a?(Array)
- @statuses.size / 10
- else
- @statuses.total_pages
- end
+ response.headers['x-page-size'] = TRENDING_STATUS_LIMIT
+ response.headers['x-page'] = params[:page] || 1
+ response.headers['x-total'] = @statuses.size
+ response.headers['x-total-pages'] = @statuses.total_pages
end
end
Only in truth-new/opensource/app/controllers/api/v1/admin: trending_tags_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/admin: truth
Only in truth-new/opensource/app/controllers/api/v1/admin: tv
diff -ru truth-old/opensource/app/controllers/api/v1/blocks_controller.rb truth-new/opensource/app/controllers/api/v1/blocks_controller.rb
--- truth-old/opensource/app/controllers/api/v1/blocks_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/blocks_controller.rb 2024-04-01 14:59:13
@@ -17,7 +17,7 @@
end
def paginated_blocks
- @paginated_blocks ||= Block.eager_load(target_account: :account_stat)
+ @paginated_blocks ||= Block.eager_load(target_account: [:account_follower, :account_following, :account_status])
.joins(:target_account)
.merge(Account.without_suspended)
.where(account: current_account)
diff -ru truth-old/opensource/app/controllers/api/v1/endorsements_controller.rb truth-new/opensource/app/controllers/api/v1/endorsements_controller.rb
--- truth-old/opensource/app/controllers/api/v1/endorsements_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/endorsements_controller.rb 2024-04-01 14:59:13
@@ -25,7 +25,7 @@
end
def endorsed_accounts
- current_account.endorsed_accounts.includes(:account_stat).without_suspended
+ current_account.endorsed_accounts.includes(:account_follower, :account_following, :account_status).without_suspended
end
def insert_pagination_headers
diff -ru truth-old/opensource/app/controllers/api/v1/favourites_controller.rb truth-new/opensource/app/controllers/api/v1/favourites_controller.rb
--- truth-old/opensource/app/controllers/api/v1/favourites_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/favourites_controller.rb 2023-05-05 13:42:02
@@ -21,7 +21,7 @@
end
def results
- @_results ||= account_favourites.eager_load(:status).to_a_paginated_by_id(
+ @_results ||= account_favourites.includes(:status).joins(:status).to_a_paginated_by_id(
limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)
Only in truth-new/opensource/app/controllers/api/v1: feeds_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/follow_requests_controller.rb truth-new/opensource/app/controllers/api/v1/follow_requests_controller.rb
--- truth-old/opensource/app/controllers/api/v1/follow_requests_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/follow_requests_controller.rb 2024-04-01 14:59:13
@@ -37,7 +37,7 @@
end
def default_accounts
- Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests)
+ Account.without_suspended.includes(:follow_requests, :account_follower, :account_following, :account_status).references(:follow_requests)
end
def paginated_follow_requests
Only in truth-new/opensource/app/controllers/api/v1: groups
Only in truth-new/opensource/app/controllers/api/v1: groups_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/lists/accounts_controller.rb truth-new/opensource/app/controllers/api/v1/lists/accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/lists/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/lists/accounts_controller.rb 2024-04-01 14:59:13
@@ -37,9 +37,9 @@
def load_accounts
if unlimited?
- @list.accounts.without_suspended.includes(:account_stat).all
+ @list.accounts.without_suspended.includes(:account_follower, :account_following, :account_status).all
else
- @list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
+ @list.accounts.without_suspended.includes(:account_follower, :account_following, :account_status).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
end
end
Only in truth-old/opensource/app/controllers/api/v1: ma1sd
diff -ru truth-old/opensource/app/controllers/api/v1/media_controller.rb truth-new/opensource/app/controllers/api/v1/media_controller.rb
--- truth-old/opensource/app/controllers/api/v1/media_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/media_controller.rb 2024-04-01 14:59:13
@@ -28,7 +28,8 @@
private
def status_code_for_media_attachment
- @media_attachment.not_processed? ? 206 : 200
+ # @media_attachment.not_processed? ? 206 : 200
+ 200
end
def set_media_attachment
diff -ru truth-old/opensource/app/controllers/api/v1/notifications_controller.rb truth-new/opensource/app/controllers/api/v1/notifications_controller.rb
--- truth-old/opensource/app/controllers/api/v1/notifications_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/notifications_controller.rb 2024-04-01 14:59:13
@@ -10,12 +10,12 @@
def index
@notifications = load_notifications
- render json: @notifications, each_serializer: REST::NotificationSerializer, relationships: StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
+ render json: @notifications, each_serializer: REST::NotificationSerializer, current_user: current_user, relationships: StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
end
def show
@notification = current_account.notifications.without_suspended.find(params[:id])
- render json: @notification, serializer: REST::NotificationSerializer
+ render json: @notification, serializer: REST::NotificationSerializer, current_user: current_user
end
def clear
@@ -31,17 +31,23 @@
private
def load_notifications
- notifications = browserable_account_notifications.includes(from_account: :account_stat).to_a_paginated_by_id(
+ notifications = browserable_account_notifications.includes(from_account: [:account_follower, :account_following, :account_status]).to_a_paginated_by_id(
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)
- Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
+ notification_with_statuses = Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
cache_collection(target_statuses, Status)
end
+
+ Notification.exclude_self_statuses(notification_with_statuses)
end
def browserable_account_notifications
- current_account.notifications.without_suspended.browserable(exclude_types, from_account)
+ current_account.notifications.without_suspended.browserable(
+ types: Array(browserable_params[:types]),
+ exclude_types: Array(browserable_params[:exclude_types]),
+ from_account_id: browserable_params[:account_id]
+ )
end
def target_statuses_from_notifications
@@ -53,7 +59,7 @@
end
def next_path
- unless @notifications.empty?
+ if records_continue?
api_v1_notifications_url pagination_params(max_id: pagination_max_id)
end
end
@@ -72,19 +78,15 @@
@notifications.first.id
end
- def exclude_types
- val = params.permit(exclude_types: [])[:exclude_types] || []
- val = [val] unless val.is_a?(Enumerable)
- val_with_groups = val.clone
- val.each { |n| val_with_groups << "#{n}_group"}
- val_with_groups
+ def browserable_params
+ params.permit(:account_id, types: [], exclude_types: [])
end
- def from_account
- params[:account_id]
+ def pagination_params(core_params)
+ params.slice(:limit, :account_id, :types, :exclude_types).permit(:limit, :account_id, types: [], exclude_types: []).merge(core_params)
end
- def pagination_params(core_params)
- params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params)
+ def records_continue?
+ @notifications.size == limit_param(DEFAULT_NOTIFICATIONS_LIMIT)
end
end
Only in truth-new/opensource/app/controllers/api/v1: pleroma
diff -ru truth-old/opensource/app/controllers/api/v1/polls/votes_controller.rb truth-new/opensource/app/controllers/api/v1/polls/votes_controller.rb
--- truth-old/opensource/app/controllers/api/v1/polls/votes_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/polls/votes_controller.rb 2024-04-01 14:59:13
@@ -9,16 +9,28 @@
def create
VoteService.new.call(current_account, @poll, vote_params[:choices])
- render json: @poll, serializer: REST::PollSerializer
+ update_optimistic_data
+ render json: REST::V2::PollSerializer.new(context: { optimistic_data: @optimistic_data, current_user: current_user }).serialize(@poll)
end
private
def set_poll
- @poll = Poll.attached.find(params[:poll_id])
+ @poll = Poll.find(params[:poll_id])
+ @optimistic_data = { votes_count: @poll.votes_count.to_i, voters_count: @poll.voters_count.to_i, own_votes: @poll.own_votes(current_account), options: @poll.loaded_options }
authorize @poll.status, :show?
rescue Mastodon::NotPermittedError
not_found
+ end
+
+ def update_optimistic_data
+ @optimistic_data[:voters_count] += 1
+ vote_params[:choices].each do |choice|
+ @optimistic_data[:votes_count] += 1
+ @optimistic_data[:own_votes].push(choice.to_i) unless @optimistic_data[:own_votes].include?(choice.to_i)
+ @optimistic_data[:options][choice.to_i][:votes_count] += 1 if @optimistic_data[:options][choice.to_i]
+ end
+ @optimistic_data[:voted] = true
end
def vote_params
diff -ru truth-old/opensource/app/controllers/api/v1/polls_controller.rb truth-new/opensource/app/controllers/api/v1/polls_controller.rb
--- truth-old/opensource/app/controllers/api/v1/polls_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/polls_controller.rb 2024-04-01 14:59:13
@@ -5,7 +5,6 @@
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, only: :show
before_action :set_poll
- before_action :refresh_poll
def show
render json: @poll, serializer: REST::PollSerializer, include_results: true
@@ -14,13 +13,9 @@
private
def set_poll
- @poll = Poll.attached.find(params[:id])
+ @poll = Poll.find(params[:id])
authorize @poll.status, :show?
rescue Mastodon::NotPermittedError
not_found
- end
-
- def refresh_poll
- ActivityPub::FetchRemotePollService.new.call(@poll, current_account) if user_signed_in? && @poll.possibly_stale?
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/push/subscriptions_controller.rb truth-new/opensource/app/controllers/api/v1/push/subscriptions_controller.rb
--- truth-old/opensource/app/controllers/api/v1/push/subscriptions_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/push/subscriptions_controller.rb 2024-04-01 14:59:13
@@ -1,29 +1,24 @@
# frozen_string_literal: true
class Api::V1::Push::SubscriptionsController < Api::BaseController
+
before_action -> { doorkeeper_authorize! :push }
before_action -> { require_user!(requires_approval: false) }
+ before_action :set_log_level
+
before_action :set_push_subscription
- before_action :check_push_subscription, only: [:show, :update]
+ before_action :check_push_subscription, only: [:show]
+ before_action :create_new_record, only: [:update]
skip_before_action :require_functional!, only: [:create]
+ after_action :revert_log_level
+ DEBUG_ACCOUNT_ID = ENV.fetch('DEBUG_ACCOUNT_ID', 0).to_i
+
def create
@push_subscription&.destroy!
remove_old_subscriptions_for_device!
-
- @push_subscription = Web::PushSubscription.create!(
- endpoint: subscription_params[:endpoint],
- device_token: subscription_params[:device_token],
- platform: subscription_params[:platform] || 0,
- environment: subscription_params[:environment] || 0,
- key_p256dh: subscription_params.dig(:keys, :p256dh),
- key_auth: subscription_params.dig(:keys, :auth),
- data: data_params,
- user_id: current_user.id,
- access_token_id: doorkeeper_token.id
- )
-
+ @push_subscription = create_push_subscription
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
end
@@ -32,7 +27,7 @@
end
def update
- @push_subscription.update!(data: data_params)
+ @push_subscription.update!(data: data_params, device_token: subscription_params[:device_token] || nil)
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
end
@@ -43,6 +38,20 @@
private
+ def create_push_subscription
+ Web::PushSubscription.create!(
+ endpoint: subscription_params[:endpoint],
+ device_token: subscription_params[:device_token],
+ platform: subscription_params[:platform] || 0,
+ environment: subscription_params[:environment] || 0,
+ key_p256dh: subscription_params.dig(:keys, :p256dh),
+ key_auth: subscription_params.dig(:keys, :auth),
+ data: data_params,
+ user_id: current_user.id,
+ access_token_id: doorkeeper_token.id
+ )
+ end
+
def set_push_subscription
@push_subscription = Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
end
@@ -51,6 +60,12 @@
not_found if @push_subscription.nil?
end
+ def create_new_record
+ return if @push_subscription
+ remove_old_subscriptions_for_device!
+ @push_subscription = create_push_subscription
+ end
+
def subscription_params
params.require(:subscription).permit(:endpoint, :device_token, :platform, :environment, keys: [:auth, :p256dh])
end
@@ -58,10 +73,34 @@
def data_params
return {} if params[:data].blank?
- params.require(:data).permit(:policy, alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status, :user_approved, :verify_sms_prompt])
+ params.require(:data).permit(:policy, alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status, :user_approved, :verify_sms_prompt, :chat, :group_favourite, :group_reblog, :group_mention, :group_approval, :group_delete, :group_role, :group_request, :group_promoted, :group_demoted])
end
def remove_old_subscriptions_for_device!
- Web::PushSubscription.destroy_by(device_token: subscription_params[:device_token]) if subscription_params[:platform].to_i > 0 && subscription_params[:device_token].present?
+ if subscription_params[:platform].to_i > 0 && subscription_params[:device_token].present?
+ duplicate_subs = Web::PushSubscription
+ .where("access_token_id != ?", doorkeeper_token.id)
+ .where(
+ device_token: subscription_params[:device_token],
+ platform: subscription_params[:platform],
+ user_id: current_user.id
+ )
+
+ duplicate_subs.destroy_all
+ end
end
+
+
+ def set_log_level
+ return unless current_account.id == DEBUG_ACCOUNT_ID
+ Rails.logger.info("Subscription logs: #{params.inspect}")
+ @current_log_level = Rails.logger.level
+ Rails.logger.level = :debug
+ end
+
+ def revert_log_level
+ return unless current_account.id == DEBUG_ACCOUNT_ID
+ Rails.logger.level = @current_log_level || :info
+ end
+
end
Only in truth-new/opensource/app/controllers/api/v1: push_notifications
Only in truth-new/opensource/app/controllers/api/v1: recommendations
diff -ru truth-old/opensource/app/controllers/api/v1/reports_controller.rb truth-new/opensource/app/controllers/api/v1/reports_controller.rb
--- truth-old/opensource/app/controllers/api/v1/reports_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/reports_controller.rb 2024-04-12 16:08:26
@@ -1,8 +1,13 @@
# frozen_string_literal: true
class Api::V1::ReportsController < Api::BaseController
+ include Authorization
+
before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
before_action :require_user!
+ before_action :set_group, only: [:create], if: -> { report_params[:group_id] }
+ before_action :set_external_ad, only: [:create], if: -> { report_params[:external_ad_media_url] }
+ before_action :check_for_existing_report, only: [:create]
override_rate_limit_headers :create, family: :reports
@@ -13,7 +18,10 @@
status_ids: reported_status_ids,
comment: report_params[:comment],
forward: report_params[:forward],
- rule_ids: reported_rule_ids
+ rule_ids: reported_rule_ids,
+ message_ids: reported_message_ids,
+ group_id: group_id,
+ external_ad_id: external_ad_id
)
render json: @report, serializer: REST::ReportSerializer
@@ -22,13 +30,34 @@
private
def reported_status_ids
- reported_account.statuses.with_discarded.find(status_ids).pluck(:id)
+ return unless @group.nil? && @external_ad.nil?
+ find_statuses.pluck(:id)
end
+ def group_id
+ return @group.id if @group
+
+ group = Group.find_by(id: find_statuses.pick(:group_id))
+ authorize group, :show? if group
+ group&.id
+ end
+
+ def external_ad_id
+ return @external_ad.id if @external_ad
+ end
+
+ def find_statuses
+ @statuses ||= reported_account.statuses.with_discarded.find(status_ids)
+ end
+
def reported_rule_ids
Rule.find(rule_ids).pluck(:id)
end
+ def reported_message_ids
+ ChatMessage.visible_messages(report_params[:account_id].to_i, "{#{message_ids.map(&:to_i).join(',')}}")
+ end
+
def status_ids
Array(report_params[:status_ids])
end
@@ -37,11 +66,61 @@
Array(report_params[:rule_ids])
end
+ def message_ids
+ Array(report_params[:message_ids])
+ end
+
def reported_account
- Account.find(report_params[:account_id])
+ if report_params[:group_id]
+ GroupMembership.find_by!(group_id: report_params[:group_id], role: 'owner').account
+ elsif @external_ad
+ Account.find(ENV.fetch('TS_ADVERTISTING_ACCOUNT_ID', nil))
+ else
+ Account.find(report_params[:account_id])
+ end
end
def report_params
- params.permit(:account_id, :comment, :forward, status_ids: [], rule_ids: [])
+ params.permit(:account_id, :comment, :forward, :group_id, :external_ad_url, :external_ad_media_url, :external_ad_description, status_ids: [], rule_ids: [], message_ids: [])
end
+
+ def set_group
+ @group = Group.find(report_params[:group_id])
+ end
+
+ def set_external_ad
+ @external_ad = ExternalAd.find_or_create_by(media_url: report_params[:external_ad_media_url], description: report_params[:external_ad_description]) do |ad|
+ ad.ad_url = report_params[:external_ad_url]
+ end
+ end
+
+ def check_for_existing_report
+ existing_reports =
+ if @group
+ current_account.reports.where(target_account: reported_account, group_id: group_id, status_ids: [])
+ elsif @external_ad
+ current_account.reports.where(target_account: reported_account, external_ad_id: external_ad_id, status_ids: [])
+ else
+ current_account.reports.where(target_account: reported_account, status_ids: reported_status_ids, message_ids: reported_message_ids)
+ end
+
+ entity =
+ if status_ids.any?
+ 'Truth'
+ elsif message_ids.any?
+ 'message'
+ elsif group_id
+ 'group'
+ elsif external_ad_id
+ 'ad'
+ else
+ 'user'
+ end
+
+ e = "Thanks, but you have already reported this #{entity}."
+
+ render json: { error: e }, status: 422 if existing_reports.any?
+ end
end
+
+
diff -ru truth-old/opensource/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb truth-new/opensource/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb 2024-04-01 14:59:13
@@ -17,20 +17,23 @@
def load_accounts
scope = default_accounts
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
- scope.merge(paginated_favourites).to_a
+ scope = scope.merge(paginated_favourites).to_a
+ @size = scope.size
+
+ @size > limit_param(DEFAULT_ACCOUNTS_LIMIT) ? scope.drop(1) : scope
end
def default_accounts
Account
.without_suspended
- .includes(:favourites, :account_stat)
+ .includes(:favourites, :account_follower, :account_following, :account_status)
.references(:favourites)
.where(favourites: { status_id: @status.id })
end
def paginated_favourites
Favourite.paginate_by_max_id(
- limit_param(DEFAULT_ACCOUNTS_LIMIT),
+ limit_param(DEFAULT_ACCOUNTS_LIMIT) + 1,
params[:max_id],
params[:since_id]
)
@@ -61,7 +64,7 @@
end
def records_continue?
- @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ @size > limit_param(DEFAULT_ACCOUNTS_LIMIT)
end
def set_status
diff -ru truth-old/opensource/app/controllers/api/v1/statuses/favourites_controller.rb truth-new/opensource/app/controllers/api/v1/statuses/favourites_controller.rb
--- truth-old/opensource/app/controllers/api/v1/statuses/favourites_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/statuses/favourites_controller.rb 2024-04-01 14:59:13
@@ -2,16 +2,22 @@
class Api::V1::Statuses::FavouritesController < Api::BaseController
include Authorization
+ include Divergable
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
before_action :require_user!
+ before_action :diverge_users_without_current_ip, only: [:create]
before_action :set_status, only: [:create]
+ after_action :create_device_verification_favourite, only: :create
+ include Assertable
+
def create
cached_status = cache_collection([@status], Status).first
- FavouriteService.new.call(current_account, @status)
- cached_status.status_stat.favourites_count = cached_status.favourites_count + 1
- render json: cached_status, serializer: REST::StatusSerializer, replica_reads: ['reblogged', 'muted', 'bookmarked']
+ @favourite = FavouriteService.new.call(current_account, @status, user_agent: request.user_agent)
+ cached_status.status_favourite || cached_status.build_status_favourite
+ cached_status.status_favourite.favourites_count = cached_status.favourites_count + 1
+ render json: cached_status, serializer: REST::StatusSerializer, replica_reads: ['reblogged', 'muted', 'bookmarked'], relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => true })
end
def destroy
@@ -19,13 +25,18 @@
if fav
@status = fav.status
+ cached_status = cache_collection([@status], Status).first
+ cached_status.status_favourite || cached_status.build_status_favourite
+ cached_status.status_favourite.favourites_count = cached_status.favourites_count - 1
+
UnfavouriteWorker.perform_async(current_account.id, @status.id)
else
@status = Status.find(params[:status_id])
+ cached_status = @status
authorize @status, :show?
end
- render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false })
+ render json: cached_status, serializer: REST::StatusSerializer, replica_reads: ['reblogged', 'muted', 'bookmarked'], relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false })
rescue Mastodon::NotPermittedError
not_found
end
@@ -37,5 +48,25 @@
authorize @status, :show?
rescue Mastodon::NotPermittedError
not_found
+ end
+
+ def validate_client
+ action_assertable?
+ end
+
+ def asserting?
+ request.headers['x-tru-assertion'] && action_assertable?
+ end
+
+ def action_assertable?
+ %w(create).include?(action_name) ? true : false
+ end
+
+ def log_android_activity?
+ current_user.user_sms_reverification_required && action_assertable?
+ end
+
+ def create_device_verification_favourite
+ DeviceVerificationFavourite.insert(verification_id: @device_verification.id, favourite_id: @favourite.id) if @device_verification && @favourite
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/statuses/mutes_controller.rb truth-new/opensource/app/controllers/api/v1/statuses/mutes_controller.rb
--- truth-old/opensource/app/controllers/api/v1/statuses/mutes_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/statuses/mutes_controller.rb 2024-04-01 14:59:13
@@ -3,11 +3,28 @@
class Api::V1::Statuses::MutesController < Api::BaseController
include Authorization
- before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
+ before_action -> { doorkeeper_authorize! :write, :'write:mutes' }, only: [:create, :destroy]
+ before_action -> { doorkeeper_authorize! :write, :'read:mutes' }, only: :index
before_action :require_user!
- before_action :set_status
- before_action :set_conversation
+ before_action :set_status, only: [:create, :destroy]
+ before_action :set_conversation, only: [:create, :destroy]
+ after_action :insert_pagination_headers, only: :index
+ MUTED_CONVERSATIONS_LIMIT = 20
+
+ def index
+ @statuses = load_muted_conversations
+
+ render json: Panko::ArraySerializer.new(
+ @statuses,
+ each_serializer: REST::V2::StatusSerializer,
+ context: {
+ current_user: current_user,
+ relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
+ }
+ ).to_json
+ end
+
def create
current_account.mute_conversation!(@conversation)
@mutes_map = { @conversation.id => true }
@@ -34,5 +51,36 @@
def set_conversation
@conversation = @status.conversation
raise Mastodon::ValidationError if @conversation.nil?
+ end
+
+ def load_muted_conversations
+ scope = paginated_conversations
+ @size = scope.size
+ @size > limit_param(MUTED_CONVERSATIONS_LIMIT) ? scope.take(limit_param(MUTED_CONVERSATIONS_LIMIT)) : scope
+ end
+
+ def paginated_conversations
+ Status.muted_conversations_for_account(current_account.id).paginate_by_limit_offset(
+ limit_param(MUTED_CONVERSATIONS_LIMIT) + 1,
+ params_slice(:offset)
+ )
+ end
+
+ def insert_pagination_headers
+ set_pagination_headers(next_path)
+ end
+
+ def next_path
+ return unless records_continue?
+
+ api_v1_mutes_url pagination_params(offset: @statuses.size + params[:offset].to_i)
+ end
+
+ def records_continue?
+ @size > limit_param(MUTED_CONVERSATIONS_LIMIT)
+ end
+
+ def pagination_params(core_params)
+ params.slice(:limit).permit(:limit).merge(core_params)
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb truth-new/opensource/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb 2024-04-01 14:59:13
@@ -21,11 +21,11 @@
end
def default_accounts
- Account.without_suspended.includes(:statuses, :account_stat).references(:statuses)
+ Account.without_suspended.includes(:statuses, :account_follower, :account_following, :account_status).references(:statuses)
end
def paginated_statuses
- Status.where(reblog_of_id: @status.id).where(visibility: [:public, :unlisted]).paginate_by_max_id(
+ Status.where(reblog_of_id: @status.id).where(visibility: [:public, :unlisted, :group]).paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT),
params[:max_id],
params[:since_id]
diff -ru truth-old/opensource/app/controllers/api/v1/statuses/reblogs_controller.rb truth-new/opensource/app/controllers/api/v1/statuses/reblogs_controller.rb
--- truth-old/opensource/app/controllers/api/v1/statuses/reblogs_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/statuses/reblogs_controller.rb 2024-04-01 14:59:13
@@ -2,16 +2,23 @@
class Api::V1::Statuses::ReblogsController < Api::BaseController
include Authorization
+ include Divergable
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
before_action :require_user!
+ before_action :diverge_users_without_current_ip, only: [:create]
before_action :set_reblog, only: [:create]
+ after_action :create_device_verification_status, only: :create
+ include Assertable
+
override_rate_limit_headers :create, family: :statuses
def create
@status = ReblogService.new.call(current_account, @reblog, reblog_params)
+ @status.reblog.status_reblog || @status.reblog.build_status_reblog
+ @status.reblog.status_reblog.reblogs_count = @status.reblog.reblogs_count + 1
render json: @status, serializer: REST::StatusSerializer
end
@@ -21,8 +28,9 @@
if @status
authorize @status, :unreblog?
@status.discard
- RemovalWorker.perform_async(@status.id, immediate: true)
+ ReblogRemovalWorker.perform_async(@status.id, immediate: true)
@reblog = @status.reblog
+ InteractionsTracker.new(current_account.id, @reblog.account_id, :reblog, current_account.following?(@reblog.account_id), @reblog.group).untrack
else
@reblog = Status.find(params[:status_id])
authorize @reblog, :show?
@@ -43,6 +51,26 @@
end
def reblog_params
- params.permit(:visibility)
+ params.permit(:visibility).merge(user_agent: request.user_agent)
+ end
+
+ def validate_client
+ action_assertable?
+ end
+
+ def asserting?
+ request.headers['x-tru-assertion'] && action_assertable?
+ end
+
+ def action_assertable?
+ %w(create).include?(action_name) ? true : false
+ end
+
+ def log_android_activity?
+ current_user.user_sms_reverification_required && action_assertable?
+ end
+
+ def create_device_verification_status
+ DeviceVerificationStatus.insert(verification_id: @device_verification.id, status_id: @status.id) if @device_verification && @status
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/statuses_controller.rb truth-new/opensource/app/controllers/api/v1/statuses_controller.rb
--- truth-old/opensource/app/controllers/api/v1/statuses_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/statuses_controller.rb 2024-04-12 09:09:08
@@ -2,6 +2,9 @@
class Api::V1::StatusesController < Api::BaseController
include Authorization
+ include Divergable
+ include Redisable
+ include AdsConcern
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy]
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy]
@@ -9,8 +12,14 @@
before_action :set_status, only: [:show, :context, :ancestors, :descendants]
before_action :set_thread, only: [:create]
before_action :require_authenticated_user!, unless: :allowed_public_access?
+ before_action :diverge_users_without_current_ip, only: [:create]
+ before_action :set_group, only: [:create]
+ before_action :reject_duplicate_group_status, only: [:create]
after_action :insert_pagination_headers, only: :descendants
+ after_action :create_device_verification_status, only: :create
+ include Assertable
+
override_rate_limit_headers :create, family: :statuses
# This API was originally unlimited, pagination cannot be introduced without
@@ -19,10 +28,17 @@
# than this anyway
CONTEXT_LIMIT = 4_096
PAGINATED_LIMIT = 20
+ STATUS_HASH_CACHE_EXPIRE_AFTER = 1.hour.seconds
+ DUPLICATE_THRESHOLD = 3
def show
@status = cache_collection([@status], Status).first
- render json: @status, serializer: REST::StatusSerializer
+
+ if (@status.visibility == 'self' && current_user.account_id != @status.account.id) || @status.group&.discarded?
+ raise(ActiveRecord::RecordNotFound)
+ end
+
+ render json: REST::V2::StatusSerializer.new(context: { current_user: current_user }).serialize(@status)
end
def context
@@ -34,6 +50,8 @@
def descendants
@descendants = prepare_descendants(PAGINATED_LIMIT)
+ include_ad_indexes(@descendants)
+
render_context_subitems(@descendants)
end
@@ -46,6 +64,9 @@
end
def create
+ whitelisted_visibilities = ['public', 'group', nil]
+ render json: { error: 'This action is not allowed' }, status: 403 and return unless whitelisted_visibilities.include?(status_params[:visibility])
+
@status = PostStatusService.new.call(current_user.account,
text: status_params[:status],
mentions: status_params[:to],
@@ -54,25 +75,30 @@
sensitive: status_params[:sensitive],
spoiler_text: status_params[:spoiler_text],
visibility: status_params[:visibility],
+ group: @group,
+ group_timeline_visible: status_params[:group_timeline_visible],
+ group_visibility: @group_visibility || nil,
scheduled_at: status_params[:scheduled_at],
application: doorkeeper_token.application,
poll: status_params[:poll],
quote_id: status_params[:quote_id],
idempotency: request.headers['Idempotency-Key'],
- with_rate_limit: true)
+ with_rate_limit: true,
+ ip_address: request.remote_ip,
+ domain: Addressable::URI.parse(request.url).normalized_host)
- render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
+ render json: REST::V2::StatusSerializer.new(context: { current_user: current_user }).serialize(@status)
end
def destroy
@status = Status.where(account_id: current_user.account).find(params[:id])
authorize @status, :destroy?
-
@status.reblogs.update_all(deleted_at: Time.current, deleted_by_id: current_user&.account_id)
@status.update!(deleted_at: Time.current, deleted_by_id: current_user&.account_id)
- RemovalWorker.perform_async(@status.id, redraft: true)
+ @thread = Status.find_by(id: @status.in_reply_to_id) if @status.in_reply_to_id
+ RemovalWorker.perform_async(@status.id, redraft: true, called_by_id: current_account.id)
remove_from_whale_list if @status.account.whale?
- @status.account.statuses_count = @status.account.statuses_count - 1
+ @status.status_pins&.destroy_all
render json: @status, serializer: REST::StatusSerializer, source_requested: true
end
@@ -83,7 +109,7 @@
@status = Status.find(params[:id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
- not_found
+ raise ActiveRecord::RecordNotFound
end
def set_thread
@@ -92,6 +118,27 @@
render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404
end
+ def set_group
+ quoted = status_params[:quote_id].presence && Status.find(status_params[:quote_id])
+ group_id = status_params[:group_id].presence || quoted&.group&.id || @thread&.group&.id
+ group = Group.find_by(id: group_id) if group_id
+ @group = if group&.discarded?
+ false
+ elsif quoted
+ quoted.group&.everyone? ? group_member?(group) && group : group # We don't want to set group if quoted group is a public group and the "quoter" is not a member.
+ else
+ group
+ end
+
+ if @group.present?
+ policy = status_params[:quote_id].present? ? :show? : :post?
+ authorize(@group, policy)
+ @group_visibility = @group.statuses_visibility
+ end
+ rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
+ render json: { error: I18n.t('statuses.errors.not_permitted_to_post') }, status: 404
+ end
+
def status_params
params.permit(
:status,
@@ -99,13 +146,14 @@
:sensitive,
:spoiler_text,
:visibility,
+ :group_id,
+ :group_timeline_visible,
:scheduled_at,
:quote_id,
to: [],
media_ids: [],
poll: [
:multiple,
- :hide_totals,
:expires_in,
options: [],
]
@@ -143,5 +191,48 @@
def allowed_public_access?
current_user || (action_name == 'show' && @status&.account&.user&.unauth_visibility? && !@status&.reply?)
+ end
+
+ def validate_client
+ action_assertable?
+ end
+
+ def asserting?
+ request.headers['x-tru-assertion'] && action_assertable?
+ end
+
+ def action_assertable?
+ %w(create).include?(action_name) ? true : false
+ end
+
+ def log_android_activity?
+ current_user&.user_sms_reverification_required && action_assertable?
+ end
+
+ def create_device_verification_status
+ DeviceVerificationStatus.insert(verification_id: @device_verification.id, status_id: @status.id) if @device_verification && @status
+ end
+
+ def group_member?(group)
+ group&.members&.where(id: current_account&.id)&.exists?
+ end
+
+ def reject_duplicate_group_status
+ return if @group.blank?
+ return if status_params[:status].blank?
+
+ status_hash = hexdigest status_params[:status]
+ key = "status:#{current_account.id}:#{status_hash}"
+ # cached_value = redis.get(key).to_i
+ # configuration = ::Configuration::FeatureSetting.find_by(name: 'rate_limit_duplicate_group_status_enabled')
+ # render json: { error: I18n.t('errors.429') }, status: 429 and return if cached_value.to_i >= DUPLICATE_THRESHOLD && ActiveModel::Type::Boolean.new.cast(configuration&.value)
+
+ redis.incrby(key, 1)
+ redis.expire(key, STATUS_HASH_CACHE_EXPIRE_AFTER)
+
+ cached_value = redis.get(key).to_i
+ if cached_value.to_i >= DUPLICATE_THRESHOLD
+ Rails.logger.info "Groups rate limit: User -> #{current_user.id} has exceeded the threshold. Current hits -> #{cached_value.to_i}, remote_ip -> #{request.remote_ip}"
+ end
end
end
Only in truth-new/opensource/app/controllers/api/v1: tags
Only in truth-new/opensource/app/controllers/api/v1: tags_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/timelines: group_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/timelines: group_tag_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/timelines/home_controller.rb truth-new/opensource/app/controllers/api/v1/timelines/home_controller.rb
--- truth-old/opensource/app/controllers/api/v1/timelines/home_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/timelines/home_controller.rb 2024-04-01 14:59:13
@@ -6,14 +6,23 @@
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
def show
- @statuses = load_statuses
+ @statuses = load_statuses
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
- render json: @statuses,
- each_serializer: REST::StatusSerializer,
- relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
- account_relationships: AccountRelationshipsPresenter.new(account_ids, current_user&.account_id),
- status: account_home_feed.regenerating? ? 206 : 200
+ if (ad_indexes = ENV.fetch('X_TRUTH_AD_INDEXES', nil))
+ response.headers['x-truth-ad-indexes'] = ad_indexes
+ end
+
+ render json: Panko::ArraySerializer.new(
+ @statuses,
+ each_serializer: REST::V2::StatusSerializer,
+ context: {
+ current_user: current_user,
+ relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
+ account_relationships: AccountRelationshipsPresenter.new(account_ids, current_user&.account_id),
+ status: account_home_feed.regenerating? ? 206 : 200,
+ }
+ ).to_json
end
private
diff -ru truth-old/opensource/app/controllers/api/v1/trends_controller.rb truth-new/opensource/app/controllers/api/v1/trends_controller.rb
--- truth-old/opensource/app/controllers/api/v1/trends_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/trends_controller.rb 2024-04-01 14:59:13
@@ -2,14 +2,47 @@
class Api::V1::TrendsController < Api::BaseController
before_action :set_tags
+ after_action :insert_pagination_headers, only: :index
+ DEFAULT_LIMIT = 20
+
def index
- render json: @tags, each_serializer: REST::TagSerializer
+ render json: @tags || []
end
private
def set_tags
- @tags = TrendingTags.get(limit_param(100))
+ @tags = TrendingTagsResult.load_results(
+ limit, # in_limit
+ offset # in_offset
+ )
+ end
+
+ def limit
+ params[:limit].present? ? params[:limit].to_i : DEFAULT_LIMIT
+ end
+
+ def offset
+ params[:offset].present? ? params[:offset].to_i : 0
+ end
+
+ def insert_pagination_headers
+ @tags = JSON.parse(@tags || '[]')
+ set_pagination_headers(next_path)
+ end
+
+ def next_path
+ if records_continue?
+ api_v1_trends_url pagination_params(offset: @tags.size + params[:offset].to_i)
+ end
+ end
+
+ def records_continue?
+ @tags.size == limit_param(DEFAULT_LIMIT)
+ end
+
+ def pagination_params(core_params)
+ params.slice(:limit, :page).permit(:limit, :page).merge(core_params)
end
end
diff -ru truth-old/opensource/app/controllers/api/v1/truth/admin/accounts_controller.rb truth-new/opensource/app/controllers/api/v1/truth/admin/accounts_controller.rb
--- truth-old/opensource/app/controllers/api/v1/truth/admin/accounts_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/truth/admin/accounts_controller.rb 2024-04-12 16:08:26
@@ -1,15 +1,23 @@
# frozen_string_literal: true
class Api::V1::Truth::Admin::AccountsController < Api::BaseController
+ include EmailHelper
+
before_action :require_staff!
- before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:count, :update]
- before_action :set_account, only: [:update]
+ before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :blacklist, :count, :email_domain_blocks]
+ before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, only: [:update, :confirm_totp]
+ before_action :set_account, only: [:update, :confirm_totp]
+ before_action :set_email_domain_block, only: :email_domain_blocks
def index
- accounts = Account.includes(:account_stat, :user).ransack(params[:query])
- accounts.sorts = params[:sorts] || "id desc"
- accounts = accounts.result.page(params[:page])
- render json: accounts, each_serializer: REST::Admin::AccountSerializer
+ @accounts = account_search
+ render json: Panko::ArraySerializer.new(
+ @accounts,
+ each_serializer: REST::V2::Admin::AccountSerializer,
+ context: {
+ advertisers: Account.recent_advertisers(@accounts.pluck(:id)),
+ }
+ ).to_json
end
def update
@@ -22,32 +30,84 @@
end
end
+ def blacklist
+ render json: { blacklist: suspended_accounts_exist? }, status: 200
+ end
+
def count
render json: { count: number_of_accounts }, status: 200
end
+ def email_domain_blocks
+ render json: { disposable: !!@email_domain_block.disposable }, status: 200
+ end
+
+ def confirm_totp
+ unless @account.user.validate_and_consume_otp!(totp_params[:code])
+ render json: {
+ error_code: 'OTP_CODE_INVALID',
+ error_message: I18n.t('otp_authentication.invalid_code'),
+ }, status: 422
+ end
+ end
+
private
+ def account_search
+ oauth_token = params[:oauth_token]
+ if oauth_token.present?
+ find_by_token(oauth_token)
+ else
+ AdminAccountSearchService.new.call(
+ params[:query],
+ current_account,
+ limit: limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ )
+ end
+ end
+
+ def suspended_accounts_exist?
+ if Account.joins(:user).where.not(suspended_at: nil).where(user: { sms: blacklist_params[:sms] }).exists?
+ 1
+ else
+ 0
+ end
+ end
+
def number_of_accounts
- if count_params[:email].present?
- User.find_by(email: count_params[:email]).present? ? 1 : 0
+ if (email = count_params[:email])
+ count_by_email(email)
elsif count_params[:sms].present?
- User.where(sms: count_params[:sms]).size
+ # For admin users, allow unlimited accounts
+ if User.where(admin: true).where(sms: count_params[:sms]).exists?
+ 0
+ else
+ User.where(sms: count_params[:sms]).size
+ end
else
0
end
end
+ def count_by_email(email)
+ User.find_by(email: email).present? || CanonicalEmailBlock.block?(email) || UserBaseEmail.find_by(email: email_to_canonical_email(email)).present? ? 1 : 0
+ end
+
+ def blacklist_params
+ params.permit(:sms)
+ end
+
def count_params
params.permit(:email, :sms)
end
def account_params
- params.require(:account).permit(:username, :display_name, :note)
+ params.require(:account).permit(:username, :display_name, :note, :website)
end
def set_account
- @account = Account.find(params[:id])
+ account_id = params[:account_id] || params[:id]
+ @account = Account.find(account_id)
end
def set_new_email
@@ -61,12 +121,15 @@
render json: { status: :success }
end
- # TODO: Vlad to refactor share access-token removal
+ def set_email_domain_block
+ @email_domain_block = EmailDomainBlock.find_by!(domain: params.require(:domain))
+ end
+
def update_users_password
@account.user.skip_password_change_notification!
if @account.user.reset_password(params[:password], params[:password])
- Doorkeeper::AccessToken.where(resource_owner_id: @account.user.id).delete_all
+ OauthAccessToken.where(resource_owner_id: @account.user.id).delete_all
@account.user.session_activations.destroy_all
@account.user.forget_me!
render json: { status: :success }, status: 200
@@ -79,5 +142,14 @@
else
render json: @account.errors, status: :unprocessable_entity
end
+ end
+
+ def find_by_token(oauth_token)
+ doorkeeper_token = OauthAccessToken.find_by!(token: oauth_token, revoked_at: nil)
+ [User.find(doorkeeper_token&.resource_owner_id)&.account]
+ end
+
+ def totp_params
+ params.permit(:code)
end
-end
\ No newline at end of file
+end
Only in truth-new/opensource/app/controllers/api/v1/truth/admin: channel_notifications_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth/admin: email_domain_blocks_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth/admin: marketing_notifications_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth/admin: media_attachments_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth: ads_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth: android_device_check
Only in truth-new/opensource/app/controllers/api/v1/truth: carousels
Only in truth-new/opensource/app/controllers/api/v1/truth: ios_device_check
Only in truth-new/opensource/app/controllers/api/v1/truth: oauth_tokens_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/truth/passwords_controller.rb truth-new/opensource/app/controllers/api/v1/truth/passwords_controller.rb
--- truth-old/opensource/app/controllers/api/v1/truth/passwords_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/truth/passwords_controller.rb 2024-04-01 14:59:13
@@ -5,13 +5,29 @@
before_action :set_confirm_user, only: :reset_confirm
before_action :set_request_user, only: :reset_request
before_action :validate_user_is_present, only: :reset_confirm
+ around_action :set_locale, only: :reset_confirm
def reset_confirm
if @user.reset_password(password_reset_confirm_params[:password], password_reset_confirm_params[:password])
update_users_password
render json: { status: :success }
else
- render json: { error: 'Password and password confirmation do not match.' }, status: 400
+ errors = @user.errors.to_hash
+ password_invalid = errors[:password]&.pop
+ default_error = I18n.t('users.password_mismatch', locale: :en)
+ message, message_with_locale, code =
+ if password_invalid.present?
+ error = errors[:base]&.pop || default_error
+ [error, password_invalid, 'PASSWORD_INVALID']
+ else
+ [default_error, I18n.t('users.password_mismatch'), 'PASSWORD_MISMATCH']
+ end
+
+ render json: {
+ error: message,
+ error_code: code,
+ error_message: message_with_locale,
+ }, status: 400
end
end
@@ -22,7 +38,7 @@
private
def validate_user_is_present
- forbidden unless @user.present?
+ forbidden if @user.blank?
end
def update_users_password
Only in truth-new/opensource/app/controllers/api/v1/truth: policies_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth: suggestions
Only in truth-new/opensource/app/controllers/api/v1/truth/trending: group_tags_controller.rb
Only in truth-new/opensource/app/controllers/api/v1/truth/trending: groups_controller.rb
diff -ru truth-old/opensource/app/controllers/api/v1/truth/trending/truths_controller.rb truth-new/opensource/app/controllers/api/v1/truth/trending/truths_controller.rb
--- truth-old/opensource/app/controllers/api/v1/truth/trending/truths_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/v1/truth/trending/truths_controller.rb 2024-04-01 14:59:13
@@ -1,33 +1,75 @@
# frozen_string_literal: true
class Api::V1::Truth::Trending::TruthsController < Api::BaseController
- before_action :set_trendings
- before_action :set_truths
+ before_action -> { doorkeeper_authorize! :read }
+ before_action :require_user!
+ after_action :insert_pagination_headers
- TRENDING_TAGS_LIMIT = 10
+ TRENDING_TRUTHS_LIMIT = 10
+ TRENDING_TRUTHS_DEFAULT_OFFSET = 10
def index
- render json: @truths, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@truths, current_user&.account_id)
+ render json: Panko::ArraySerializer.new(
+ truths,
+ each_serializer: REST::V2::StatusSerializer,
+ context: {
+ current_user: current_user,
+ relationships: StatusRelationshipsPresenter.new(@truths, current_user&.account_id),
+ }
+ ).to_json
end
private
- def set_trendings
- @trendings = Trending.limit(TRENDING_TAGS_LIMIT).all
+ def trending_truths
+ @trending_truths ||= Status.trending_statuses
+ .excluding_unauthorized_tv_statuses(current_account.id)
+ .paginate_by_limit_offset(
+ limit_param(TRENDING_TRUTHS_LIMIT),
+ params_slice(:offset)
+ )
end
- def set_truths
- @truths = load_statuses
+ def truths
+ @truths ||= load_cached_tagged_statuses
end
- def load_statuses
- cached_tagged_statuses
+ def load_cached_tagged_statuses
+ cache_collection(trending_truths, Status)
end
- def cached_tagged_statuses
- cache_collection(all_trending_timeline_statuses, Status)
+ def insert_pagination_headers
+ set_pagination_headers(next_path, prev_path)
end
- def all_trending_timeline_statuses
- @trendings.flat_map(&:status)
+ def next_path
+ api_v1_truth_trending_truths_url offset: max_pagination_offset if records_continue?
+ end
+
+ def prev_path
+ no_prev_path? ? nil : api_v1_truth_trending_truths_url(offset: min_pagination_offset)
+ end
+
+ def no_prev_path?
+ trending_truths.empty? || params[:offset]&.to_i&.zero? || !params[:offset]
+ end
+
+ def max_pagination_offset
+ params[:offset] ? params[:offset].to_i + TRENDING_TRUTHS_DEFAULT_OFFSET.to_i : TRENDING_TRUTHS_DEFAULT_OFFSET
+ end
+
+ def min_pagination_offset
+ params[:offset] ? params[:offset].to_i - TRENDING_TRUTHS_DEFAULT_OFFSET.to_i : nil
+ end
+
+ def pagination_max_id
+ trending_truths.last.id
+ end
+
+ def pagination_since_id
+ trending_truths.first.id
+ end
+
+ def records_continue?
+ trending_truths.size == limit_param(TRENDING_TRUTHS_LIMIT)
end
end
Only in truth-new/opensource/app/controllers/api/v1/truth: videos_controller.rb
Only in truth-new/opensource/app/controllers/api/v1: tv
Only in truth-new/opensource/app/controllers/api/v1: verify_sms
Only in truth-new/opensource/app/controllers/api/v2: feeds_controller.rb
Only in truth-new/opensource/app/controllers/api/v2: pleroma
Only in truth-new/opensource/app/controllers/api/v2: statuses_controller.rb
Only in truth-new/opensource/app/controllers/api: v4
diff -ru truth-old/opensource/app/controllers/api/web/push_subscriptions_controller.rb truth-new/opensource/app/controllers/api/web/push_subscriptions_controller.rb
--- truth-old/opensource/app/controllers/api/web/push_subscriptions_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/api/web/push_subscriptions_controller.rb 2024-04-01 14:59:13
@@ -26,6 +26,7 @@
mention: alerts_enabled,
poll: alerts_enabled,
status: alerts_enabled,
+ chat: alerts_enabled
},
}
@@ -61,6 +62,6 @@
end
def data_params
- @data_params ||= params.require(:data).permit(:policy, alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status])
+ @data_params ||= params.require(:data).permit(:policy, alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status, :chat])
end
end
Only in truth-new/opensource/app/controllers: apidocs_controller.rb
diff -ru truth-old/opensource/app/controllers/application_controller.rb truth-new/opensource/app/controllers/application_controller.rb
--- truth-old/opensource/app/controllers/application_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/application_controller.rb 2024-04-12 09:09:08
@@ -24,6 +24,7 @@
rescue_from ActionController::UnknownFormat, with: :not_acceptable
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
+ rescue_from Mastodon::UnprocessableEntityError, with: :unprocessable_with_message
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable
@@ -99,6 +100,10 @@
respond_with_error(422)
end
+ def unprocessable_with_message(error_message)
+ render json: { error: error_message.to_s }, status: 422
+ end
+
def not_acceptable
respond_with_error(406)
end
@@ -144,10 +149,21 @@
'mastodon-light'
end
- def respond_with_error(code)
+ def respond_with_error(status)
+ code = Rack::Utils::HTTP_STATUS_CODES[status]
respond_to do |format|
- format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] }
- format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
+ format.json do
+ render json: {
+ error: code,
+ error_message: code,
+ error_code: format_code(code),
+ }, status: status
+ end
+ format.any { render "errors/#{status}", layout: 'error', status: status, formats: [:html] }
end
+ end
+
+ def format_code(string)
+ string.upcase.gsub(' ', '_')
end
end
diff -ru truth-old/opensource/app/controllers/auth/passwords_controller.rb truth-new/opensource/app/controllers/auth/passwords_controller.rb
--- truth-old/opensource/app/controllers/auth/passwords_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/auth/passwords_controller.rb 2024-04-01 14:59:13
@@ -11,7 +11,7 @@
if resource.errors.empty?
resource.session_activations.destroy_all
resource.forget_me!
- Doorkeeper::AccessToken.where(resource_owner_id: resource.id).update_all(revoked_at: Time.now.utc)
+ OauthAccessToken.where(resource_owner_id: resource.id).update_all(revoked_at: Time.now.utc)
end
end
end
Only in truth-new/opensource/app/controllers/concerns: ads_concern.rb
Only in truth-new/opensource/app/controllers/concerns: assertable.rb
diff -ru truth-old/opensource/app/controllers/concerns/cache_concern.rb truth-new/opensource/app/controllers/concerns/cache_concern.rb
--- truth-old/opensource/app/controllers/concerns/cache_concern.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/concerns/cache_concern.rb 2024-04-01 14:59:13
@@ -28,7 +28,7 @@
response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature'
end
- def cache_collection(raw, klass)
+ def cache_collection(raw, klass, include_removed = false)
return raw unless klass.respond_to?(:with_includes)
raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
@@ -37,13 +37,25 @@
cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys
+ raw_hash = raw.index_by(&:id)
+
+ if klass.has_attribute?(:tombstone)
+ cached_keys_with_value = reload_tombstone_value(cached_keys_with_value, raw_hash)
+ end
+
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
unless uncached_ids.empty?
- uncached = klass.where(id: uncached_ids).with_includes.index_by(&:id)
+ uncached = klass
+ uncached = uncached.with_discarded if include_removed
+ uncached = uncached.where(id: uncached_ids).with_includes.index_by(&:id)
+ if klass.has_attribute?(:tombstone)
+ uncached = reload_tombstone_value(uncached, raw_hash)
+ end
+
uncached.each_value do |item|
- Rails.cache.write(item, item, expires_in: 60.minutes)
+ Rails.cache.write(item, item, expires_in: 1.hour)
end
end
@@ -52,5 +64,13 @@
def cache_collection_paginated_by_id(raw, klass, limit, options)
cache_collection raw.cache_ids.to_a_paginated_by_id(limit, options), klass
+ end
+
+ def reload_tombstone_value(collection, raw_hash)
+ collection.each_with_object({}) do |(k, v), hash|
+ next unless v.has_attribute?(:tombstone) && raw_hash[k].has_attribute?(:tombstone)
+ hash[k] = [v.tombstone = raw_hash[k].tombstone]
+ end
+ collection
end
end
Only in truth-new/opensource/app/controllers/concerns: clientable.rb
Only in truth-new/opensource/app/controllers/concerns: divergable.rb
diff -ru truth-old/opensource/app/controllers/concerns/status_controller_concern.rb truth-new/opensource/app/controllers/concerns/status_controller_concern.rb
--- truth-old/opensource/app/controllers/concerns/status_controller_concern.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/concerns/status_controller_concern.rb 2024-04-01 14:59:13
@@ -38,10 +38,7 @@
descendants = cache_collection(
@status.descendants(
DESCENDANTS_LIMIT,
- current_account,
- @max_descendant_thread_id,
- @since_descendant_thread_id,
- DESCENDANTS_DEPTH_LIMIT
+ current_account
),
Status
)
diff -ru truth-old/opensource/app/controllers/concerns/two_factor_authentication_concern.rb truth-new/opensource/app/controllers/concerns/two_factor_authentication_concern.rb
--- truth-old/opensource/app/controllers/concerns/two_factor_authentication_concern.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/concerns/two_factor_authentication_concern.rb 2024-04-01 14:59:13
@@ -39,8 +39,6 @@
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s
restart_session
- elsif user.webauthn_enabled? && user_params.key?(:credential) && session[:attempt_user_id]
- authenticate_with_two_factor_via_webauthn(user)
elsif user_params.key?(:otp_attempt) && session[:attempt_user_id]
authenticate_with_two_factor_via_otp(user)
elsif user.present? && user.external_or_valid_password?(user_params[:password])
diff -ru truth-old/opensource/app/controllers/concerns/user_tracking_concern.rb truth-new/opensource/app/controllers/concerns/user_tracking_concern.rb
--- truth-old/opensource/app/controllers/concerns/user_tracking_concern.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/concerns/user_tracking_concern.rb 2024-04-01 14:59:13
@@ -1,9 +1,11 @@
# frozen_string_literal: true
module UserTrackingConcern
+ include Redisable
+
extend ActiveSupport::Concern
- TRACKED_CONTROLLERS = %w(home credentials)
- UPDATE_SIGN_IN_HOURS = 24
+ TRACKED_CONTROLLERS = %w(credentials)
+ INTERACTIONS_SCORE_TRACKED_CONTROLLER = 'credentials'
included do
before_action :update_user_sign_in
@@ -12,10 +14,27 @@
private
def update_user_sign_in
- current_user.update_sign_in!(request) if user_needs_sign_in_update?
+ if user_needs_sign_in_update?
+ current_user.update_sign_in!(request)
+ update_account_score
+ end
end
def user_needs_sign_in_update?
- TRACKED_CONTROLLERS.include?(controller_name) && user_signed_in? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < UPDATE_SIGN_IN_HOURS.hours.ago)
+ TRACKED_CONTROLLERS.include?(controller_name) && user_signed_in? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < Date.today)
+ end
+
+ def update_account_score
+ return unless controller_name == INTERACTIONS_SCORE_TRACKED_CONTROLLER && current_account
+
+ current_week = Time.now.strftime('%U').to_i
+ last_week = current_week - 1
+ key1 = "interactions_score:#{current_account.id}:#{current_week}"
+ key2 = "interactions_score:#{current_account.id}:#{last_week}"
+
+ scores = Redis.current.mget(key1, key2)
+ scores_sum = scores.compact.map(&:to_i).sum.to_i
+
+ current_account.update(interactions_score: scores_sum) if scores_sum
end
end
Only in truth-new/opensource/app/controllers: link_controller.rb
diff -ru truth-old/opensource/app/controllers/oauth/mfa_controller.rb truth-new/opensource/app/controllers/oauth/mfa_controller.rb
--- truth-old/opensource/app/controllers/oauth/mfa_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/oauth/mfa_controller.rb 2023-05-05 13:42:02
@@ -5,6 +5,7 @@
def challenge
params[:grant_type] = "password"
+ request.params[:username] = false
create
end
diff -ru truth-old/opensource/app/controllers/settings/deletes_controller.rb truth-new/opensource/app/controllers/settings/deletes_controller.rb
--- truth-old/opensource/app/controllers/settings/deletes_controller.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/controllers/settings/deletes_controller.rb 2024-04-01 14:59:13
@@ -43,7 +43,9 @@
def destroy_account!
current_account.suspend!(origin: :local)
- AccountDeletionWorker.perform_async(current_user.account_id)
+ acct_id = current_account.id
+ # Self deletion uses acct_id as the deleted_by_id
+ AccountDeletionWorker.perform_async(acct_id, acct_id, skip_activitypub: true)
sign_out
end
end
Only in truth-new/opensource/app/controllers/well_known: skadnetwork_controller.rb
diff -ru truth-old/opensource/app/helpers/application_helper.rb truth-new/opensource/app/helpers/application_helper.rb
--- truth-old/opensource/app/helpers/application_helper.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/helpers/application_helper.rb 2024-04-01 14:59:13
@@ -92,7 +92,7 @@
elsif status.private_visibility? || status.limited_visibility?
fa_icon('lock', title: I18n.t('statuses.visibilities.private'))
elsif status.direct_visibility?
- fa_icon('envelope', title: I18n.t('statuses.visibilities.direct'))
+ fa_icon('at', title: I18n.t('statuses.visibilities.direct'))
end
end
Only in truth-new/opensource/app/helpers: discarded_helper.rb
diff -ru truth-old/opensource/app/helpers/email_helper.rb truth-new/opensource/app/helpers/email_helper.rb
--- truth-old/opensource/app/helpers/email_helper.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/helpers/email_helper.rb 2024-04-01 14:59:13
@@ -1,15 +1,27 @@
# frozen_string_literal: true
module EmailHelper
+ BASE_EMAIL_DOMAINS_VALIDATION_STRIP_DOTS = ENV.fetch('BASE_EMAIL_DOMAINS_VALIDATION_STRIP_DOTS', false)
+
def self.included(base)
base.extend(self)
end
def email_to_canonical_email(str)
+ username, domain = email_to_canonical_email_by_username_and_domain(str).values_at(:username, :domain)
+ "#{username}@#{domain}"
+ end
+
+ def email_to_canonical_email_by_username_and_domain(str)
username, domain = str.downcase.split('@', 2)
- username, = username.gsub('.', '').split('+', 2)
- "#{username}@#{domain}"
+ if BASE_EMAIL_DOMAINS_VALIDATION_STRIP_DOTS && BASE_EMAIL_DOMAINS_VALIDATION_STRIP_DOTS.split(',').map(&:strip).include?(domain)
+ username = username.gsub('.', '')
+ end
+
+ username, = username.split('+', 2)
+
+ { username: username, domain: domain }
end
def email_to_canonical_email_hash(str)
diff -ru truth-old/opensource/app/helpers/statuses_helper.rb truth-new/opensource/app/helpers/statuses_helper.rb
--- truth-old/opensource/app/helpers/statuses_helper.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/helpers/statuses_helper.rb 2024-04-01 14:59:13
@@ -101,7 +101,7 @@
when 'private'
fa_icon 'lock fw'
when 'direct'
- fa_icon 'envelope fw'
+ fa_icon 'at fw'
end
end
diff -ru truth-old/opensource/app/javascript/mastodon/components/status.js truth-new/opensource/app/javascript/mastodon/components/status.js
--- truth-old/opensource/app/javascript/mastodon/components/status.js 2022-06-08 09:15:38
+++ truth-new/opensource/app/javascript/mastodon/components/status.js 2024-04-01 14:59:13
@@ -457,8 +457,6 @@
'direct': { icon: 'envelope', text: intl.formatMessage(messages.direct_short) },
};
- const visibilityIcon = visibilityIconInfo[status.get('visibility')];
-
return (
<HotKeys handlers={handlers}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
Only in truth-new/opensource/app/javascript/mastodon/locales/locale-data: README.md
diff -ru truth-old/opensource/app/javascript/mastodon/selectors/index.js truth-new/opensource/app/javascript/mastodon/selectors/index.js
--- truth-old/opensource/app/javascript/mastodon/selectors/index.js 2022-06-08 09:15:38
+++ truth-new/opensource/app/javascript/mastodon/selectors/index.js 2024-04-01 14:59:13
@@ -3,12 +3,11 @@
import { me } from '../initial_state';
const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
-const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
const getAccountMoved = (state, id) => state.getIn(['accounts', state.getIn(['accounts', id, 'moved'])]);
export const makeGetAccount = () => {
- return createSelector([getAccountBase, getAccountCounters, getAccountRelationship, getAccountMoved], (base, counters, relationship, moved) => {
+ return createSelector([getAccountBase, getAccountRelationship, getAccountMoved], (base, counters, relationship, moved) => {
if (base === null) {
return null;
}
diff -ru truth-old/opensource/app/javascript/packs/public.js truth-new/opensource/app/javascript/packs/public.js
--- truth-old/opensource/app/javascript/packs/public.js 2022-06-08 09:15:38
+++ truth-new/opensource/app/javascript/packs/public.js 2024-04-01 14:59:13
@@ -318,6 +318,10 @@
}
});
});
+
+ delegate(document, '[data-behavior="close-window"]', 'click', () => {
+ window.open('', '_parent', '').close();
+ });
}
loadPolyfills()
diff -ru truth-old/opensource/app/javascript/styles/application.scss truth-new/opensource/app/javascript/styles/application.scss
--- truth-old/opensource/app/javascript/styles/application.scss 2022-06-08 09:15:38
+++ truth-new/opensource/app/javascript/styles/application.scss 2024-04-01 14:59:13
@@ -28,3 +28,5 @@
@import 'mastodon/rtl';
@import 'mastodon/accessibility';
@import 'mastodon/inbox';
+@import 'mastodon/links';
+@import 'mastodon/utils';
Only in truth-new/opensource/app/javascript/styles: docs.scss
Only in truth-new/opensource/app/javascript/styles/mastodon: links.scss
Only in truth-new/opensource/app/javascript/styles/mastodon: utils.scss
diff -ru truth-old/opensource/app/lib/access_token_extension.rb truth-new/opensource/app/lib/access_token_extension.rb
--- truth-old/opensource/app/lib/access_token_extension.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/access_token_extension.rb 2024-04-01 14:59:13
@@ -3,6 +3,8 @@
module AccessTokenExtension
extend ActiveSupport::Concern
+ include Paginable
+
included do
after_commit :push_to_streaming_api
end
diff -ru truth-old/opensource/app/lib/activitypub/activity/create.rb truth-new/opensource/app/lib/activitypub/activity/create.rb
--- truth-old/opensource/app/lib/activitypub/activity/create.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/activitypub/activity/create.rb 2024-04-01 15:02:56
@@ -111,7 +111,7 @@
thread: replied_to_status,
conversation: conversation_from_uri(@object['conversation']),
media_attachment_ids: process_attachments.take(4).map(&:id),
- poll: process_poll,
+ polls: process_poll || [],
quote: quote_from_url(@object['quoteUrl']),
}
end
@@ -284,15 +284,13 @@
items = @object['oneOf']
end
- voters_count = @object['votersCount']
+ poll_options = items.map.with_index { |v, i| { option_number: i, text: v['name'] } }
- @account.polls.new(
+ [Poll.new(
multiple: multiple,
expires_at: expires_at,
- options: items.map { |item| item['name'].presence || item['content'] }.compact,
- cached_tallies: items.map { |item| item.dig('replies', 'totalItems') || 0 },
- voters_count: voters_count
- )
+ options_attributes: poll_options
+ )]
end
def poll_vote?
@@ -313,7 +311,6 @@
end
increment_voters_count! unless already_voted
- ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, replied_to_status.id) unless replied_to_status.preloadable_poll.hide_totals?
end
def resolve_thread(status)
diff -ru truth-old/opensource/app/lib/activitypub/activity/delete.rb truth-new/opensource/app/lib/activitypub/activity/delete.rb
--- truth-old/opensource/app/lib/activitypub/activity/delete.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/activitypub/activity/delete.rb 2024-04-01 14:59:13
@@ -13,7 +13,13 @@
def delete_person
lock_or_return("delete_in_progress:#{@account.id}") do
- DeleteAccountService.new.call(@account, reserve_username: false, skip_activitypub: true)
+ DeleteAccountService.new.call(
+ @account,
+ DeleteAccountService::DELETED_BY_SERVICE,
+ deletion_type: 'activitypub_delete_person',
+ reserve_username: false,
+ skip_activitypub: true,
+ )
end
end
diff -ru truth-old/opensource/app/lib/activitypub/activity/update.rb truth-new/opensource/app/lib/activitypub/activity/update.rb
--- truth-old/opensource/app/lib/activitypub/activity/update.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/activitypub/activity/update.rb 2024-04-01 14:59:13
@@ -26,7 +26,5 @@
status = Status.find_by(uri: object_uri, account_id: @account.id)
return if status.nil? || status.preloadable_poll.nil?
-
- ActivityPub::ProcessPollService.new.call(status.preloadable_poll, @object)
end
end
diff -ru truth-old/opensource/app/lib/activitypub/tag_manager.rb truth-new/opensource/app/lib/activitypub/tag_manager.rb
--- truth-old/opensource/app/lib/activitypub/tag_manager.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/activitypub/tag_manager.rb 2024-04-01 14:59:13
@@ -27,6 +27,12 @@
when :note, :comment, :activity
return activity_account_status_url(target.account, target) if target.reblog?
short_account_status_url(target.account, target)
+ when :group
+ group_url(target)
+ when :group_note
+ group_status_url(target)
+ when :group_request
+ group_request_url(target)
end
end
@@ -35,12 +41,18 @@
case target.object_type
when :person
- target.instance_actor? ? instance_actor_url : account_url(target)
+ account_url(target)
when :note, :comment, :activity
return activity_account_status_url(target.account, target) if target.reblog?
account_status_url(target.account, target)
when :emoji
emoji_url(target)
+ when :group
+ group_url(target)
+ when :group_note
+ group_status_url(target)
+ when :group_request
+ group_request_url(target)
end
end
@@ -48,6 +60,12 @@
account_url(username: username)
end
+ def url_for_chat_message(id)
+ message = ChatMessage.find(id)
+ chat = message.chat
+ "#{root_url}chats/#{chat.id}/messages/#{id}"
+ end
+
def generate_uri_for(_target)
URI.join(root_url, 'payloads', SecureRandom.uuid)
end
@@ -168,5 +186,19 @@
end
rescue ActiveRecord::RecordNotFound
nil
+ end
+
+ private
+
+ def group_status_url(target)
+ "https://#{Rails.configuration.x.web_domain}/group/#{target.group.slug}/statuses/#{target.id}"
+ end
+
+ def group_request_url(target)
+ "https://#{Rails.configuration.x.web_domain}/group/#{target.group.slug}/manage/requests"
+ end
+
+ def group_url(target)
+ "https://#{Rails.configuration.x.web_domain}/group/#{target.slug}"
end
end
Only in truth-new/opensource/app/lib: event_provider
Only in truth-new/opensource/app/lib: events
diff -ru truth-old/opensource/app/lib/feed_manager.rb truth-new/opensource/app/lib/feed_manager.rb
--- truth-old/opensource/app/lib/feed_manager.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/feed_manager.rb 2024-04-01 14:59:13
@@ -153,11 +153,11 @@
# @param [Account] into_account
# @return [void]
def unmerge_from_home(from_account, into_account)
- timeline_key = key(:home, into_account.id)
- oldest_home_score = redis_timelines.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
+ timeline_key = key(:home, into_account.id)
+ timeline_status_ids = redis.zrange(timeline_key, 0, -1)
- from_account.statuses.select('id, reblog_of_id').where('id > ?', oldest_home_score).reorder(nil).find_each do |status|
- remove_from_feed(:home, into_account.id, status, into_account.user&.aggregates_reblogs?)
+ from_account.statuses.select('id, reblog_of_id').where(id: timeline_status_ids).reorder(nil).find_each do |status|
+ remove_from_feed(:home, into_account.id, status, aggregate_reblogs: into_account.user&.aggregates_reblogs?)
end
end
@@ -166,11 +166,11 @@
# @param [List] list
# @return [void]
def unmerge_from_list(from_account, list)
- timeline_key = key(:list, list.id)
- oldest_list_score = redis_timelines.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
+ timeline_key = key(:list, list.id)
+ timeline_status_ids = redis.zrange(timeline_key, 0, -1)
- from_account.statuses.select('id, reblog_of_id').where('id > ?', oldest_list_score).reorder(nil).find_each do |status|
- remove_from_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?)
+ from_account.statuses.select('id, reblog_of_id').where(id: timeline_status_ids).reorder(nil).find_each do |status|
+ remove_from_feed(:list, list.id, status, aggregate_reblogs: list.account.user&.aggregates_reblogs?)
end
end
@@ -180,7 +180,7 @@
# @return [void]
def clear_from_home(account, target_account)
timeline_key = key(:home, account.id)
- timeline_status_ids = redis_timelines.zrange(timeline_key, 0, -1)
+ timeline_status_ids = status_ids_to_plain_numbers(redis_timelines.zrange(timeline_key, 0, -1))
statuses = Status.where(id: timeline_status_ids).select(:id, :reblog_of_id, :account_id).to_a
reblogged_ids = Status.where(id: statuses.map(&:reblog_of_id).compact, account: target_account).pluck(:id)
with_mentions_ids = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact, account: target_account).pluck(:status_id)
@@ -200,7 +200,7 @@
# @return [void]
def clear_from_list(list, target_account)
timeline_key = key(:list, list.id)
- timeline_status_ids = redis_timelines.zrange(timeline_key, 0, -1)
+ timeline_status_ids = status_ids_to_plain_numbers(redis_timelines.zrange(timeline_key, 0, -1))
statuses = Status.where(id: timeline_status_ids).select(:id, :reblog_of_id, :account_id).to_a
reblogged_ids = Status.where(id: statuses.map(&:reblog_of_id).compact, account: target_account).pluck(:id)
with_mentions_ids = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact, account: target_account).pluck(:status_id)
@@ -232,11 +232,11 @@
aggregate = account.user&.aggregates_reblogs?
timeline_key = key(:home, account.id)
- account.statuses.limit(limit).each do |status|
+ account.statuses.where.not(visibility: :group).limit(limit).each do |status|
add_to_feed(:home, account.id, status, aggregate)
end
- account.following.includes(:account_stat).find_each do |target_account|
+ account.following.includes(:account_follower, :account_following, :account_status).find_each do |target_account|
if redis_timelines.zcard(timeline_key) >= limit
oldest_home_score = redis_timelines.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
last_status_score = Mastodon::Snowflake.id_at(account.last_status_at)
@@ -282,7 +282,7 @@
# references to.
redis_timelines.pipelined do
reblogged_id_sets.each do |feed_id, future|
- future.value.each do |reblogged_id|
+ status_ids_to_plain_numbers(future.value).each do |reblogged_id|
reblog_set_key = key(type, feed_id, "reblogs:#{reblogged_id}")
redis_timelines.del(reblog_set_key)
end
@@ -310,6 +310,10 @@
redis.publish("timeline:whale:#{status.account_id}", Oj.dump(event: :delete, payload: status.id.to_s))
true
+ end
+
+ def status_ids_to_plain_numbers(status_ids)
+ status_ids.map { |id| (id.is_a? Integer) || ((id.is_a? String) && id.force_encoding('UTF-8').valid_encoding? && (!id.include? '\\')) ? id : id.reverse.unpack('q').first }
end
private
diff -ru truth-old/opensource/app/lib/formatter.rb truth-new/opensource/app/lib/formatter.rb
--- truth-old/opensource/app/lib/formatter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/formatter.rb 2024-04-01 14:59:13
@@ -33,9 +33,11 @@
linkable_accounts = get_linkable_accounts(status)
linkable_accounts << status.account
+ external_links = options[:external_links] ? { external_links: options[:external_links].index_by(&:url) } : {}
+
html = raw_content
html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
- html = encode_and_link_urls(html, linkable_accounts)
+ html = encode_and_link_urls(html, linkable_accounts, external_links)
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
html = simple_format(html, {}, sanitize: false)
html = quotify(html, status) if status.quote? && !options[:escape_quotify]
@@ -103,6 +105,21 @@
html.html_safe # rubocop:disable Rails/OutputSafety
end
+ def format_chat_message(message)
+ linkable_usernames = []
+
+ message.scan(Account::MENTION_RE).each do |match|
+ username = match[1]
+ linkable_usernames << username
+ end
+
+ linkable_accounts = Account.ci_find_by_usernames(linkable_usernames).to_a
+
+ html = encode_and_link_urls(message, linkable_accounts)
+ html = simple_format(html, {}, sanitize: false)
+ html.html_safe # rubocop:disable Rails/OutputSafety
+ end
+
def linkify(text)
html = encode_and_link_urls(text)
html = simple_format(html, {}, sanitize: false)
@@ -155,7 +172,8 @@
def count_tag_nesting(tag)
if tag[1] == '/' then -1
elsif tag[-2] == '/' then 0
- else 1
+ else
+ 1
end
end
@@ -282,8 +300,11 @@
end
def link_to_url(entity, options = {})
- url = Addressable::URI.parse(entity[:url])
-
+ url = if options[:external_links] && (link_id = options[:external_links][entity[:url]]&.id)
+ link_url(link_id, subdomain: 'links')
+ else
+ Addressable::URI.parse(entity[:url])
+ end
html_attrs = { target: '_blank', rel: 'nofollow noopener noreferrer' }
html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
Only in truth-new/opensource/app/lib: hostile_rate_limiter.rb
diff -ru truth-old/opensource/app/lib/inline_renderer.rb truth-new/opensource/app/lib/inline_renderer.rb
--- truth-old/opensource/app/lib/inline_renderer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/inline_renderer.rb 2024-04-01 14:59:13
@@ -21,11 +21,13 @@
serializer = REST::ReactionSerializer
when :encrypted_message
serializer = REST::EncryptedMessageSerializer
+ when :chat
+ serializer = REST::ChatSerializer
else
return
end
- serializable_resource = ActiveModelSerializers::SerializableResource.new(@object, serializer: serializer, scope: current_user, scope_name: :current_user)
+ serializable_resource = ActiveModelSerializers::SerializableResource.new(@object, serializer: serializer, scope: current_user, scope_name: :current_user, current_user: @current_account&.user)
serializable_resource.as_json
end
Only in truth-new/opensource/app/lib: interactions_tracker.rb
Only in truth-old/opensource/app/lib: potential_friendship_tracker.rb
diff -ru truth-old/opensource/app/lib/prometheus/application_exporter.rb truth-new/opensource/app/lib/prometheus/application_exporter.rb
--- truth-old/opensource/app/lib/prometheus/application_exporter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/prometheus/application_exporter.rb 2024-04-01 14:59:13
@@ -18,19 +18,37 @@
follows: 'number of accounts following account',
unfollows: 'number of accounts unfollowing accounts',
links: 'number of posted links',
- approves: 'number of approved users'
+ approves: 'number of approved users',
+ ad_impressions: 'number of ad impressions',
+ chats: 'number of chats',
+ chat_messages: 'number of chat messages',
}
+ @histogram_instances = {}
+ histogram_metrics = {
+ video_passthrough_encoding: 'duration for processing passthrough video encoding',
+ }
+
prometheus_client = PrometheusExporter::Client.default
counter_metrics.each do |key, value|
@counter_instances[key] = prometheus_client.register(:counter, key, value)
end
+ histogram_metrics.each do |key, value|
+ @histogram_instances[key] = prometheus_client.register(:histogram, key, value)
+ end
+
def increment(metric, labels = {})
return if Rails.env.test? || Rails.env.development?
- @counter_instances[metric]&.increment(labels)
+ @counter_instances[metric]&.increment(labels)
+ end
+
+ def observe_duration(metric, duration, labels = {})
+ return if Rails.env.test? || Rails.env.development?
+
+ @histogram_instances[metric]&.observe(duration, labels)
end
end
-end
\ No newline at end of file
+end
Only in truth-new/opensource/app/lib: queue_manager.rb
diff -ru truth-old/opensource/app/lib/rate_limiter.rb truth-new/opensource/app/lib/rate_limiter.rb
--- truth-old/opensource/app/lib/rate_limiter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/rate_limiter.rb 2023-11-29 12:30:01
@@ -5,7 +5,7 @@
FAMILIES = {
follows: {
- limit: 400,
+ limit: 250,
period: 24.hours.freeze,
}.freeze,
@@ -23,8 +23,8 @@
def initialize(by, options = {})
@by = by
@family = options[:family]
- @limit = FAMILIES[@family][:limit]
- @period = FAMILIES[@family][:period].to_i
+ @limit = self.class::FAMILIES[@family][:limit]
+ @period = self.class::FAMILIES[@family][:period].to_i
end
def record!
@@ -32,16 +32,21 @@
if count.nil?
redis.set(key, 0)
+ count = 0
redis.expire(key, (@period - (last_epoch_time % @period) + 1).to_i)
end
- raise Mastodon::RateLimitExceededError, "Rate limit hit by RateLimiter #{@family}" if count.present? && count.to_i >= @limit && ENV['SKIP_IP_RATE_LIMITING'] != 'true'
+ raise error, "Rate limit hit by #{self.class.name} #{@family}" if count.to_i >= @limit && ENV['SKIP_IP_RATE_LIMITING'] != 'true'
redis.incr(key)
end
def rollback!
redis.decr(key)
+ end
+
+ def error
+ Mastodon::RateLimitExceededError
end
def to_headers(now = Time.now.utc)
diff -ru truth-old/opensource/app/lib/request.rb truth-new/opensource/app/lib/request.rb
--- truth-old/opensource/app/lib/request.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/request.rb 2024-04-01 14:59:13
@@ -12,6 +12,27 @@
@socket = socket_class.open(host, port)
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
end
+
+ def reset_counter
+ @deadline = nil
+ end
+
+ def readpartial(size, buffer = nil)
+ @deadline ||= Process.clock_gettime(Process::CLOCK_MONOTONIC) + @read_timeout
+
+ timeout = false
+ loop do
+ result = @socket.read_nonblock(size, buffer, exception: false)
+
+ return :eof if result.nil?
+
+ remaining_time = @deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ raise HTTP::TimeoutError, "Read timed out after #{@read_timeout} seconds" if timeout || remaining_time <= 0
+ return result if result != :wait_readable
+
+ timeout = true unless @socket.to_io.wait_readable(remaining_time)
+ end
+ end
end
class Request
@@ -101,10 +122,9 @@
private
def set_common_headers!
- parsed_url = Addressable::URI.parse(@url)
@headers[REQUEST_TARGET] = "#{@verb} #{@url.path}"
@headers['User-Agent'] = Mastodon::Version.user_agent
- @headers['Host'] = "#{@url.host}:#{parsed_url.inferred_port}"
+ @headers['Host'] = "#{@url.host}"
@headers['Date'] = Time.now.utc.httpdate
@headers['Accept-Encoding'] = 'gzip' if @verb != :head
end
@@ -142,7 +162,7 @@
end
def use_proxy?(url)
- parsed = URI.parse(url)
+ parsed = URI.parse(Addressable::URI.encode(url))
return false if private_address?(parsed.host)
Rails.configuration.x.http_client_proxy.present?
end
@@ -161,6 +181,7 @@
address = Resolv.getaddress(hostname)
[
+ IPAddr.new('127.0.0.1'),
IPAddr.new('10.0.0.0/8'),
IPAddr.new('172.16.0.0/12'),
IPAddr.new('192.168.0.0/16'),
@@ -174,7 +195,7 @@
end
module ClientLimit
- def body_with_limit(limit = 1.megabyte)
+ def body_with_limit(limit = 4.megabyte)
raise Mastodon::LengthValidationError if content_length.present? && content_length > limit
if charset.nil?
diff -ru truth-old/opensource/app/lib/status_filter.rb truth-new/opensource/app/lib/status_filter.rb
--- truth-old/opensource/app/lib/status_filter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/status_filter.rb 2024-04-01 14:59:13
@@ -3,15 +3,18 @@
class StatusFilter
attr_reader :status, :account
- def initialize(status, account, preloaded_relations = {})
+ def initialize(status, account, preloaded_relations = {}, root_status = nil, urls = [], marketing_push_notification = false)
@status = status
@account = account
@preloaded_relations = preloaded_relations
+ @root_status = root_status
+ @urls = urls
+ @marketing_push_notification = marketing_push_notification
end
def filtered?
- return false if !account.nil? && account.id == status.account_id
- blocked_by_policy? || (account_present? && filtered_status?) || silenced_account?
+ return false if !account.nil? && account.id == status.account_id && !deleted_status?
+ blocked_by_policy? || (account_present? && filtered_status?) || silenced_account? || privatized_status? || contains_recent_link? || contains_bad_link? || deleted_status?
end
private
@@ -54,5 +57,30 @@
def policy_allows_show?
StatusPolicy.new(account, status, @preloaded_relations).show?
+ end
+
+ def privatized_status?
+ status.visibility == 'self' && status.account != account
+ end
+
+ def contains_recent_link?
+ return unless @root_status
+ time_difference = (Time.now - status.created_at).round
+ delay_minutes = @marketing_push_notification ? 900 : 300
+
+ @urls.any? && !status.account.whale? && status.account != account && time_difference < delay_minutes
+ end
+
+ def contains_bad_link?
+ return unless @root_status && @marketing_push_notification && @urls.any?
+
+ time_difference = (status.created_at - @marketing_push_notification.created_at).round
+ return if time_difference > 6.hours.to_i
+
+ !status.account.whale? && status.account != account
+ end
+
+ def deleted_status?
+ status.deleted_at?
end
end
diff -ru truth-old/opensource/app/lib/status_finder.rb truth-new/opensource/app/lib/status_finder.rb
--- truth-old/opensource/app/lib/status_finder.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/lib/status_finder.rb 2024-04-01 14:59:13
@@ -3,8 +3,9 @@
class StatusFinder
attr_reader :url
- def initialize(url)
+ def initialize(url, allow_activity: false)
@url = url
+ @allowed_actions = allow_activity ? ['show', 'activity'] : ['show']
end
def status
@@ -27,8 +28,6 @@
end
def verify_action!
- unless recognized_params[:action] == 'show'
- raise ActiveRecord::RecordNotFound
- end
+ raise ActiveRecord::RecordNotFound unless @allowed_actions.include?(recognized_params[:action])
end
end
Only in truth-old/opensource/app/lib: status_reach_finder.rb
Only in truth-new/opensource/app/lib: url_placeholder.rb
diff -ru truth-old/opensource/app/mailers/notification_mailer.rb truth-new/opensource/app/mailers/notification_mailer.rb
--- truth-old/opensource/app/mailers/notification_mailer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/mailers/notification_mailer.rb 2024-04-01 14:59:13
@@ -42,9 +42,16 @@
return unless @me.user.functional? && @status.present?
+ subject =
+ if notification.count
+ I18n.t('notification_mailer.favourite_group.subject', name: @account.acct, count_others: notification.count - 1, actor: "others")
+ else
+ I18n.t('notification_mailer.favourite.subject', name: @account.acct)
+ end
+
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
- mail to: @me.user.email, subject: I18n.t('notification_mailer.favourite.subject', name: @account.acct)
+ mail to: @me.user.email, subject: subject
end
end
@@ -103,7 +110,7 @@
return unless @resource.active_for_authentication?
I18n.with_locale(@resource.locale || I18n.default_locale) do
- mail to: @resource.email, subject: I18n.t('notification_mailer.user_approved.web.subject')
+ mail to: @resource.email, subject: I18n.t('notification_mailer.user_approved.title', name: @resource.account.username)
end
end
diff -ru truth-old/opensource/app/models/account.rb truth-new/opensource/app/models/account.rb
--- truth-old/opensource/app/models/account.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/account.rb 2024-04-05 09:07:34
@@ -3,7 +3,6 @@
#
# Table name: accounts
#
-# id :bigint(8) not null, primary key
# username :string default(""), not null
# domain :string
# private_key :text
@@ -31,6 +30,7 @@
# shared_inbox_url :string default(""), not null
# followers_url :string default(""), not null
# protocol :integer default("ostatus"), not null
+# id :bigint(8) not null, primary key
# memorial :boolean default(FALSE), not null
# moved_to_account_id :bigint(8)
# featured_collection_url :string
@@ -52,6 +52,14 @@
# location :text default(""), not null
# website :text default(""), not null
# whale :boolean default(FALSE)
+# interactions_score :integer
+# file_s3_host :string(64)
+# accepting_messages :boolean default(TRUE), not null
+# chats_onboarded :boolean default(FALSE), not null
+# feeds_onboarded :boolean default(FALSE), not null
+# show_nonmember_group_statuses :boolean default(TRUE), not null
+# tv_onboarded :boolean default(FALSE), not null
+# receive_only_follow_mentions :boolean default(FALSE), not null
#
class Account < ApplicationRecord
@@ -63,6 +71,8 @@
hub_url
)
+ attribute :message_expiration, :interval
+
USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[a-z0-9]+)?)/i
@@ -83,6 +93,7 @@
TRUST_LEVELS = {
untrusted: 0,
trusted: 1,
+ hostile: -1,
}.freeze
enum protocol: [:ostatus, :activitypub]
@@ -100,9 +111,15 @@
validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
validates :note, note_length: { maximum: 500 }, if: -> { local? && will_save_change_to_note? }
validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? }
+ validates :location, length: { maximum: 500 }, if: -> { local? && will_save_change_to_location? }
+ validates :url, length: { maximum: 500 }, if: -> { local? && will_save_change_to_url? }
+ validates :website, length: { maximum: 500 }, if: -> { local? && will_save_change_to_website? }
validate :check_website_field_for_javascript
+ after_update_commit :invalidate_statuses, if: -> { saved_change_to_username? }
+ after_update_commit :invalidate_ads_cache
+
scope :remote, -> { where.not(domain: nil) }
scope :local, -> { where(domain: nil) }
scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
@@ -122,12 +139,14 @@
scope :searchable, -> { without_suspended.where(moved_to_account_id: nil) }
scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).left_outer_joins(:account_stat) }
scope :followable_by, ->(account) { joins(arel_table.join(Follow.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(Follow.arel_table[:target_account_id]).and(Follow.arel_table[:account_id].eq(account.id))).join_sources).where(Follow.arel_table[:id].eq(nil)).joins(arel_table.join(FollowRequest.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(FollowRequest.arel_table[:target_account_id]).and(FollowRequest.arel_table[:account_id].eq(account.id))).join_sources).where(FollowRequest.arel_table[:id].eq(nil)) }
- scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) }
+ # TODO: evaluate last_status_at/current_sign_in_at nulls last instead of case statements
+ scope :by_recent_status, -> { order(Arel.sql('(case when last_status_at is null then 1 else 0 end) asc, last_status_at desc, accounts.id desc')) }
scope :by_recent_sign_in, -> { order(Arel.sql('(case when users.current_sign_in_at is null then 1 else 0 end) asc, users.current_sign_in_at desc, accounts.id desc')) }
scope :popular, -> { order('account_stats.followers_count desc') }
scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches("%.#{domain}"))) }
scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
+ scope :excluded_by_group_account_block, ->(group_id) { where.not(GroupAccountBlock.where('group_account_blocks.account_id = accounts.id').where('group_account_blocks.group_id = ?', group_id).arel.exists) }
delegate :email,
:unconfirmed_email,
@@ -145,14 +164,17 @@
:locale,
:hides_network?,
:shows_application?,
+ :sms,
to: :user,
prefix: true,
allow_nil: true
delegate :chosen_languages, to: :user, prefix: false, allow_nil: true
- update_index 'accounts#account', :self
+ attr_accessor :seen
+ update_index 'accounts', :self
+
def contains_prohibited_terms?
user_and_display_name_downcase = "#{username} #{display_name}".downcase
Status::PROHIBITED_TERMS_ON_INDEX.any? { |term| user_and_display_name_downcase.include? term }
@@ -203,11 +225,11 @@
end
def to_webfinger_s
- "acct:#{local_username_and_domain}"
+ "acct:#{username}@#{Rails.configuration.x.local_domain}"
end
def searchable?
- !(suspended? || moved?)
+ !moved?
end
def possibly_stale?
@@ -252,25 +274,33 @@
update!(suspended_at: date, suspension_origin: origin)
end
create_canonical_email_block!
+ InteractionsTracker.new(id).remove_total_score
end
def unsuspend!
transaction do
deletion_request&.destroy!
update!(suspended_at: nil, suspension_origin: nil)
+ user&.enable!
destroy_canonical_email_block!
end
end
+ def deleted?
+ user.nil?
+ end
+
def verify!
transaction do
update!(verified: true)
+ user.update!(unauth_visibility: true)
end
end
def unverify!
transaction do
update!(verified: false)
+ user.update!(unauth_visibility: false)
end
end
@@ -294,6 +324,14 @@
update!(memorial: true)
end
+ def accept_messages!
+ update!(accepting_messages: true)
+ end
+
+ def unaccept_messages!
+ update!(accepting_messages: false)
+ end
+
def sign?
true
end
@@ -350,6 +388,10 @@
self[:fields] = fields
end
+ def account_fields
+ (self[:fields] || []).map { |f| AccountField.new(self, f) }
+ end
+
DEFAULT_FIELDS_SIZE = 4
def build_fields
@@ -391,7 +433,7 @@
end
def check_website_field_for_javascript
- errors.add(:base, "Please enter a valid website") if JAVASCRIPT_RE.match(website)
+ errors.add(:base, 'Please enter a valid website') if JAVASCRIPT_RE.match(website)
end
# TODO: follow_requests profile feature toggle "locked"
@@ -430,7 +472,14 @@
WhaleCacheInvalidationWorker.perform_async(id)
end
+ def tv_enabled?
+ feature_enabled? 'tv'
+ end
+ def for_you_enabled?
+ feature_enabled? 'for_you'
+ end
+
class Field < ActiveModelSerializers::Model
attributes :name, :value, :verified_at, :account
@@ -471,6 +520,45 @@
end
end
+ class AccountField
+ attr_reader :name, :value, :account
+ attr_accessor :verified_at
+
+ def initialize(account, attributes)
+ @original_field = attributes
+ string_limit = account.local? ? 255 : 2047
+ @account = account
+ @name = attributes['name'].strip[0, string_limit]
+ @value = attributes['value'].strip[0, string_limit]
+ @verified_at = attributes['verified_at']&.to_datetime
+ end
+
+ def verified?
+ verified_at.present?
+ end
+
+ def value_for_verification
+ @value_for_verification ||= if account.local?
+ value
+ else
+ ActionController::Base.helpers.strip_tags(value)
+ end
+ end
+
+ def verifiable?
+ value_for_verification.present? && value_for_verification.start_with?('http://', 'https://')
+ end
+
+ def mark_verified!
+ self.verified_at = Time.now.utc
+ @original_field['verified_at'] = verified_at
+ end
+
+ def to_h
+ { name: name, value: value, verified_at: verified_at }
+ end
+ end
+
class << self
def readonly_attributes
super - %w(statuses_count following_count followers_count)
@@ -484,13 +572,13 @@
def ci_find_by_username(username = nil)
return nil unless username.present?
- includes(:user).where("LOWER(username) = ?", username.downcase).take
+ includes(:user).find_by('LOWER(username) = ?', username.downcase)
end
def ci_find_by_usernames(usernames = [])
return Account.none if usernames.empty?
- where("LOWER(username) IN (?)", usernames.compact.map { |un| un.downcase })
+ where('LOWER(username) IN (?)', usernames.compact.map { |un| un.downcase })
end
def search_for(terms, limit = 10, offset = 0)
@@ -509,7 +597,7 @@
SQL
records = find_by_sql([sql, limit, offset])
- ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
+ ActiveRecord::Associations::Preloader.new.preload(records, [:account_follower, :account_following, :account_status, :tv_channel_account])
records
end
@@ -558,7 +646,7 @@
records = find_by_sql([sql, account.id, account.id, limit, offset])
end
- ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
+ ActiveRecord::Associations::Preloader.new.preload(records, [:account_follower, :account_following, :account_status, :tv_channel_account])
records
end
@@ -591,7 +679,31 @@
@emojis ||= CustomEmoji.from_text(emojifiable_text, domain)
end
+ def recent_ads
+ statuses.where('created_at > ?', 1.month.ago)
+ .where(in_reply_to_id: nil)
+ .where(Ad.where('statuses.id = ads.status_id').arel.exists)
+ end
+
+ # Identifies the accounts that have advertised recently based on a list of account_ids.
+ #
+ # @param [Array<Integer>] account_ids The list of account IDs to check.
+ # @return [Array<Integer>] Returns an array containing the account IDs that have advertised recently.
+ #
+ def self.recent_advertisers(account_ids, recently = 1.month.ago)
+ Status
+ .select(:account_id)
+ .where(account_id: account_ids)
+ .where('statuses.created_at > ?', recently)
+ .where(in_reply_to_id: nil)
+ .joins(:ad)
+ .group(:account_id)
+ .reorder('')
+ .pluck(:account_id)
+ end
+
before_create :generate_keys
+ before_save :set_file_s3_host, if: -> { will_save_change_to_avatar_file_name? || will_save_change_to_header_file_name? }
before_validation :prepare_contents, if: :local?
before_validation :prepare_username, on: :create
before_destroy :clean_feed_manager
@@ -631,6 +743,7 @@
def create_canonical_email_block!
return unless local? && user_email.present?
+ return if CanonicalEmailBlock.block?(user_email)
CanonicalEmailBlock.create(reference_account: self, email: user_email)
rescue ActiveRecord::RecordNotUnique
@@ -641,5 +754,38 @@
return unless local?
CanonicalEmailBlock.where(reference_account: self).delete_all
+ end
+
+ def set_file_s3_host
+ self.file_s3_host = Paperclip::Attachment.default_options[:s3_host_name]
+ end
+
+ def invalidate_statuses
+ InvalidateAccountStatusesWorker.perform_async(id)
+ end
+
+ def invalidate_ads_cache
+ InvalidateAdsAccountsWorker.perform_async(id) if OauthAccessToken.exists?(resource_owner_id: user&.id, scopes: 'ads')
+ end
+
+ def fields_changed(account)
+ updatable_fields = %w(bio display_name avatar_url header_url followers_count following_count website location username verified)
+ changed_fields = account.saved_changes.keys
+
+ updated_fields = changed_fields.select { |f| updatable_fields.include?(f) }
+ updated_fields << 'avatar_url' if changed_fields.include?('avatar_file_name')
+ updated_fields << 'header_url' if changed_fields.include?('header_file_name')
+ updated_fields.map(&:upcase)
+ end
+
+ def feature_enabled?(feature)
+ feature_flag = ::Configuration::FeatureFlag
+ .joins("LEFT JOIN configuration.account_enabled_features ON configuration.feature_flags.feature_flag_id = configuration.account_enabled_features.feature_flag_id AND configuration.account_enabled_features.account_id = #{id}")
+ .where(name: feature)
+ .select('configuration.feature_flags.name, configuration.feature_flags.status, configuration.account_enabled_features.account_id')
+ .to_a
+ .first
+
+ feature_flag&.enabled? == true || (feature_flag&.account_based? == true && !feature_flag&.account_id.nil?)
end
end
diff -ru truth-old/opensource/app/models/account_domain_block.rb truth-new/opensource/app/models/account_domain_block.rb
--- truth-old/opensource/app/models/account_domain_block.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/account_domain_block.rb 2024-04-01 14:59:13
@@ -27,6 +27,6 @@
end
def remove_relationship_cache
- Rails.cache.delete_matched("relationship:#{account_id}:*")
+ Rails.cache.delete_matched("relationship/#{account_id}/*")
end
end
Only in truth-new/opensource/app/models: account_follower_statistic.rb
Only in truth-new/opensource/app/models: account_following_statistic.rb
diff -ru truth-old/opensource/app/models/account_stat.rb truth-new/opensource/app/models/account_stat.rb
--- truth-old/opensource/app/models/account_stat.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/account_stat.rb 2024-04-01 14:59:13
@@ -18,7 +18,7 @@
belongs_to :account, inverse_of: :account_stat
- update_index('accounts#account', :account)
+ update_index('accounts', :account)
def following_count
[attributes['following_count'], 0].max
@@ -31,4 +31,4 @@
def statuses_count
[attributes['statuses_count'], 0].max
end
-end
+end
\ No newline at end of file
Only in truth-new/opensource/app/models: account_status_statistic.rb
diff -ru truth-old/opensource/app/models/account_suggestions.rb truth-new/opensource/app/models/account_suggestions.rb
--- truth-old/opensource/app/models/account_suggestions.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/account_suggestions.rb 2024-04-01 14:59:13
@@ -2,24 +2,17 @@
class AccountSuggestions
SOURCES = [
- AccountSuggestions::SettingSource,
- AccountSuggestions::PastInteractionsSource,
- AccountSuggestions::GlobalSource,
+ {klass: AccountSuggestions::SettingSource, limit: 300},
+ {klass: AccountSuggestions::PastInteractionsSource, limit: 25},
+ {klass: AccountSuggestions::GlobalSource, limit: 25}
].freeze
- # Since we iterate through 3 arrays, this number is the max # of suggestions that will be returned
- # Ex: if the total limit is 120 and the client requests 5 at a time, the total # of pages that can be returned is 24
- TOTAL_RESULTS_LIMIT = 150
-
- # The total limit divided by the # of sources
- ARRAY_LIMIT = TOTAL_RESULTS_LIMIT / SOURCES.length.floor
-
def self.get(account)
- SOURCES.each_with_object([]) do |source_class, suggestions|
- source_suggestions = source_class.new.get(
+ SOURCES.each_with_object([]) do |obj, suggestions|
+ source_suggestions = obj[:klass].new.get(
account,
skip_account_ids: suggestions.map(&:account_id),
- limit: ARRAY_LIMIT
+ limit: obj[:limit]
)
suggestions.concat(source_suggestions)
@@ -27,8 +20,8 @@
end
def self.remove(account, target_account_id)
- SOURCES.each do |source_class|
- source = source_class.new
+ SOURCES.each do |obj|
+ source = obj[:klass].new
source.remove(account, target_account_id)
end
end
Only in truth-new/opensource/app/models: ad.rb
Only in truth-new/opensource/app/models: ad_attribution.rb
diff -ru truth-old/opensource/app/models/admin/account_action.rb truth-new/opensource/app/models/admin/account_action.rb
--- truth-old/opensource/app/models/admin/account_action.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/admin/account_action.rb 2024-04-01 14:59:13
@@ -19,6 +19,9 @@
unsuspend
verify
unverify
+ enable_sms_reverification
+ disable_sms_reverification
+ enable_feature
).freeze
attr_accessor :target_account,
@@ -26,7 +29,8 @@
:type,
:text,
:report_id,
- :warning_preset_id
+ :warning_preset_id,
+ :feature_name
attr_reader :warning, :send_email_notification, :include_statuses, :duration
@@ -54,7 +58,6 @@
end
process_email!
- process_reports!
process_queue!
end
@@ -106,6 +109,12 @@
handle_remove_avatar!
when 'remove_header'
handle_remove_header!
+ when 'enable_sms_reverification'
+ handle_enable_sms_reverification!
+ when 'disable_sms_reverification'
+ handle_disable_sms_reverification!
+ when 'enable_feature'
+ handle_enable_feature!
end
end
@@ -152,18 +161,24 @@
log_action(:ban, target_account.user)
target_account.suspend!
target_account.user.disable!
+ remove_scheduled_unsuspensions
+ InteractionsTracker.new(target_account).remove_total_score
+ DisabledUserUnfollowWorker.perform_async(target_account.id)
+ revoke_access_tokens(target_account)
end
def handle_disable!
authorize(target_account.user, :disable?)
log_action(:disable, target_account.user)
target_account.user&.disable!
+ DisabledUserUnfollowWorker.perform_in(7.days, target_account.id)
end
def handle_enable!
authorize(target_account.user, :enable?)
log_action(:enable, target_account.user)
target_account.user&.enable!
+ DisabledUserRefollowWorker.perform_async(target_account.id)
end
def handle_sensitive!
@@ -188,6 +203,7 @@
authorize(target_account, :suspend?)
log_action(:suspend, target_account)
target_account.suspend!(origin: :local)
+ InteractionsTracker.new(target_account).remove_total_score
schedule_unsuspension! unless account_suspension_policy.strikes_expended?
end
@@ -196,6 +212,7 @@
authorize(target_account, :unsuspend?)
log_action(:unsuspend, target_account)
target_account.unsuspend!
+ DisabledUserRefollowWorker.perform_async(target_account.id)
end
def handle_verify!
@@ -224,16 +241,37 @@
target_account.save!
end
- def text_for_warning
- [warning_preset&.text, text].compact.join("\n\n")
+ def handle_enable_sms_reverification!
+ authorize(target_account.user, :enable_sms_reverification?)
+ log_action(:enable_sms_reverification, target_account.user)
+ user = target_account.user
+ UserSmsReverificationRequired.create(user: user) if user
end
- def queue_suspension_worker!
- Admin::SuspensionWorker.perform_async(target_account.id)
+ def handle_disable_sms_reverification!
+ authorize(target_account.user, :disable_sms_reverification?)
+ log_action(:disable_sms_reverification, target_account.user)
+ user = target_account.user
+ UserSmsReverificationRequired.find(user.id)&.destroy if user
end
+ def handle_enable_feature!
+ authorize(target_account.user, :enable_feature?)
+ feature_flag = ::Configuration::FeatureFlag.find_by!(name: feature_name)
+ ::Configuration::AccountEnabledFeature.create!(account_id: target_account.id, feature_flag: feature_flag)
+ end
+
+ def text_for_warning
+ [warning_preset&.text, text].compact.join("\n\n")
+ end
+
def process_queue!
- queue_suspension_worker! if type == 'suspend'
+ case type
+ when 'suspend', 'ban'
+ Admin::SuspensionWorker.perform_async(target_account.id)
+ when 'unsuspend'
+ Admin::UnsuspensionWorker.perform_async(target_account.id)
+ end
end
def process_email!
@@ -272,5 +310,15 @@
def account_suspension_policy
@account_suspension_policy ||= AccountSuspensionPolicy.new(target_account)
+ end
+
+ def remove_scheduled_unsuspensions
+ queue = Sidekiq::ScheduledSet.new
+ jobs = queue.select { |job| job.klass == 'Admin::UnsuspensionWorker' && job.args[0] == target_account.id }
+ jobs.each(&:delete)
+ end
+
+ def revoke_access_tokens(target_account)
+ OauthAccessToken.where(resource_owner_id: target_account.user.id).update_all(revoked_at: Time.now.utc)
end
end
diff -ru truth-old/opensource/app/models/announcement.rb truth-new/opensource/app/models/announcement.rb
--- truth-old/opensource/app/models/announcement.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/announcement.rb 2024-04-01 14:59:13
@@ -77,7 +77,7 @@
end
end
- ActiveRecord::Associations::Preloader.new.preload(records, :custom_emoji)
+ ActiveRecord::Associations::Preloader.new.preload(records, [:account_follower, :account_following, :account_status])
records
end
diff -ru truth-old/opensource/app/models/application_record.rb truth-new/opensource/app/models/application_record.rb
--- truth-old/opensource/app/models/application_record.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/application_record.rb 2024-04-01 14:59:13
@@ -7,7 +7,7 @@
class << self
def update_index(_type_name, *_args, &_block)
- super if Chewy.enabled?
+ super if Chewy.indexing_enabled?
end
end
Only in truth-new/opensource/app/models: avatars_carousel.rb
Only in truth-new/opensource/app/models: banned_word.rb
Only in truth-new/opensource/app/models: blocked_link.rb
diff -ru truth-old/opensource/app/models/bookmark.rb truth-new/opensource/app/models/bookmark.rb
--- truth-old/opensource/app/models/bookmark.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/bookmark.rb 2024-04-01 14:59:13
@@ -13,7 +13,7 @@
class Bookmark < ApplicationRecord
include Paginable
- update_index('statuses#status', :status) if Chewy.enabled?
+ update_index('statuses', :status) if Chewy.indexing_enabled?
belongs_to :account, inverse_of: :bookmarks
belongs_to :status, inverse_of: :bookmarks
Only in truth-new/opensource/app/models: chat.rb
Only in truth-new/opensource/app/models: chat_event.rb
Only in truth-new/opensource/app/models: chat_member.rb
Only in truth-new/opensource/app/models: chat_member_removal.rb
Only in truth-new/opensource/app/models: chat_message.rb
Only in truth-new/opensource/app/models: chat_message_hidden.rb
Only in truth-new/opensource/app/models: chat_message_reaction.rb
Only in truth-new/opensource/app/models: chat_search_result.rb
Only in truth-new/opensource/app/models: city.rb
diff -ru truth-old/opensource/app/models/concerns/account_associations.rb truth-new/opensource/app/models/concerns/account_associations.rb
--- truth-old/opensource/app/models/concerns/account_associations.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/account_associations.rb 2024-04-12 09:09:08
@@ -30,7 +30,7 @@
# Media
has_many :media_attachments, dependent: :destroy
- has_many :polls, dependent: :destroy
+ has_many :polls, dependent: :destroy, through: :statuses
# Report relationships
has_many :reports, dependent: :destroy, inverse_of: :account
@@ -66,5 +66,27 @@
# Follow recommendations
has_one :follow_recommendation_suppression, inverse_of: :account, dependent: :destroy
+
+ # Chats
+ has_many :chat_accounts
+ has_many :chats, -> { distinct }, through: :chat_accounts
+ has_many :chat_messages
+
+ # Groups
+ has_many :group_memberships
+ has_many :group_mutes
+
+ has_one :tv_account
+ has_one :tv_channel_account
+ has_and_belongs_to_many :tv_channels, join_table: 'tv.channel_accounts', association_foreign_key: 'channel_id', inverse_of: :account
+ # Feeds
+ has_many :account_feeds, class_name: 'Feeds::AccountFeed'
+ has_many :feeds, through: :account_feeds
+
+ # Recommendation suppressions
+ has_many :group_recommendation_suppressions, class_name: 'Recommendations::GroupSuppression'
+ has_many :account_recommendation_suppressions, class_name: 'Recommendations::AccountSuppression'
+ # Features
+ has_and_belongs_to_many :feature_flags, class_name: 'Configuration::FeatureFlag', join_table: 'configuration.account_enabled_features', association_foreign_key: 'feature_flag_id', inverse_of: :account
end
end
diff -ru truth-old/opensource/app/models/concerns/account_counters.rb truth-new/opensource/app/models/concerns/account_counters.rb
--- truth-old/opensource/app/models/concerns/account_counters.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/account_counters.rb 2024-04-01 14:59:13
@@ -3,85 +3,30 @@
module AccountCounters
extend ActiveSupport::Concern
- ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count).freeze
-
included do
- has_one :account_stat, inverse_of: :account
- after_save :save_account_stat
+ has_one :account_follower, class_name: AccountFollowerStatistic.name, inverse_of: :account
+ has_one :account_following, class_name: AccountFollowingStatistic.name, inverse_of: :account
+ has_one :account_status, class_name: AccountStatusStatistic.name, inverse_of: :account
+
end
- delegate :statuses_count,
- :statuses_count=,
- :following_count,
- :following_count=,
- :followers_count,
- :followers_count=,
- :last_status_at,
- to: :account_stat
-
- # @param [Symbol] key
- def increment_count!(key)
- update_count!(key, 1)
+ def followers_count
+ account_follower&.followers_count || 0
end
- # @param [Symbol] key
- def decrement_count!(key)
- update_count!(key, -1)
+ def following_count
+ account_following&.following_count || 0
end
- # @param [Symbol] key
- # @param [Integer] value
- def update_count!(key, value)
- raise ArgumentError, "Invalid key #{key}" unless ALLOWED_COUNTER_KEYS.include?(key)
- raise ArgumentError, 'Do not call update_count! on dirty objects' if association(:account_stat).loaded? && account_stat&.changed? && account_stat.changed_attribute_names_to_save == %w(id)
-
- value = value.to_i
- default_value = value.positive? ? value : 0
-
- # We do an upsert using manually written SQL, as Rails' upsert method does
- # not seem to support writing expressions in the UPDATE clause, but only
- # re-insert the provided values instead.
- # Even ARel seem to be missing proper handling of upserts.
- sql = if value.positive? && key == :statuses_count
- <<-SQL.squish
- INSERT INTO account_stats(account_id, #{key}, created_at, updated_at, last_status_at)
- VALUES (:account_id, :default_value, now(), now(), now())
- ON CONFLICT (account_id) DO UPDATE
- SET #{key} = account_stats.#{key} + :value,
- last_status_at = now(),
- updated_at = now()
- RETURNING id;
- SQL
- else
- <<-SQL.squish
- INSERT INTO account_stats(account_id, #{key}, created_at, updated_at)
- VALUES (:account_id, :default_value, now(), now())
- ON CONFLICT (account_id) DO UPDATE
- SET #{key} = account_stats.#{key} + :value,
- updated_at = now()
- RETURNING id;
- SQL
- end
-
- sql = AccountStat.sanitize_sql([sql, account_id: id, default_value: default_value, value: value])
- account_stat_id = AccountStat.connection.exec_query(sql)[0]['id']
-
- # Reload account_stat if it was loaded, taking into account newly-created unsaved records
- if association(:account_stat).loaded?
- account_stat.id = account_stat_id if account_stat.new_record?
- account_stat.reload
- end
+ def statuses_count
+ account_status&.statuses_count || 0
end
- def account_stat
- super || build_account_stat
+ def last_status_at
+ account_status&.last_status_at
end
- private
-
- def save_account_stat
- return unless association(:account_stat).loaded? && account_stat&.changed?
-
- account_stat.save
+ def last_following_status_at
+ account_status&.last_following_status_at
end
end
diff -ru truth-old/opensource/app/models/concerns/account_interactions.rb truth-new/opensource/app/models/concerns/account_interactions.rb
--- truth-old/opensource/app/models/concerns/account_interactions.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/account_interactions.rb 2024-04-01 14:59:13
@@ -80,6 +80,7 @@
has_many :following, -> { order('follows.id desc') }, through: :active_relationships, source: :target_account
has_many :followers, -> { order('follows.id desc') }, through: :passive_relationships, source: :account
+ has_many :followers_unordered, through: :passive_relationships, source: :account
# Block relationships
has_many :block_relationships, class_name: 'Block', foreign_key: 'account_id', dependent: :destroy
@@ -95,6 +96,10 @@
has_many :conversation_mutes, dependent: :destroy
has_many :domain_blocks, class_name: 'AccountDomainBlock', dependent: :destroy
has_many :announcement_mutes, dependent: :destroy
+
+ # Group relationships
+ has_many :group_membership_requests, dependent: :destroy
+ has_many :group_account_blocks, dependent: :destroy
end
def follow!(other_account, reblogs: nil, notify: nil, uri: nil, rate_limit: false, bypass_limit: false)
@@ -127,6 +132,7 @@
def block!(other_account, uri: nil)
remove_potential_friendship(other_account)
+ remove_follower_interactions(other_account)
block_relationships.create_with(uri: uri)
.find_or_create_by!(target_account: other_account)
end
@@ -138,6 +144,7 @@
mute.save!
remove_potential_friendship(other_account)
+ remove_follower_interactions(other_account)
# When toggling a mute between hiding and allowing notifications, the mute will already exist, so the find_or_create_by! call will return the existing Mute without updating the hide_notifications attribute. Therefore, we check that hide_notifications? is what we want and set it if it isn't.
if mute.hide_notifications? != notifications
@@ -251,26 +258,6 @@
.where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)
end
- def remote_followers_hash(url_prefix)
- Rails.cache.fetch("followers_hash:#{id}:#{url_prefix}") do
- digest = "\x00" * 32
- followers.where(Account.arel_table[:uri].matches("#{url_prefix}%", false, true)).pluck_each(:uri) do |uri|
- Xorcist.xor!(digest, Digest::SHA256.digest(uri))
- end
- digest.unpack('H*')[0]
- end
- end
-
- def local_followers_hash
- Rails.cache.fetch("followers_hash:#{id}:local") do
- digest = "\x00" * 32
- followers.where(domain: nil).pluck_each(:username) do |username|
- Xorcist.xor!(digest, Digest::SHA256.digest(ActivityPub::TagManager.instance.uri_for_username(username)))
- end
- digest.unpack('H*')[0]
- end
- end
-
def whale_following
following.where(whale: true)
end
@@ -278,7 +265,13 @@
private
def remove_potential_friendship(other_account, mutual = false)
- PotentialFriendshipTracker.remove(id, other_account.id)
- PotentialFriendshipTracker.remove(other_account.id, id) if mutual
+ InteractionsTracker.new(id, other_account.id).remove
+ InteractionsTracker.new(other_account.id, id).remove if mutual
+ end
+
+ def remove_follower_interactions(other_account)
+ InteractionsTracker.new(id, other_account.id, false, true).remove
+ Redis.current.del("avatars_carousel_list_#{id}")
+ InvalidateSecondaryCacheService.new.call("InvalidateAvatarsCarouselCacheWorker", id)
end
end
diff -ru truth-old/opensource/app/models/concerns/account_merging.rb truth-new/opensource/app/models/concerns/account_merging.rb
--- truth-old/opensource/app/models/concerns/account_merging.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/account_merging.rb 2024-04-01 14:59:13
@@ -12,16 +12,17 @@
# to check for (and skip past) uniqueness errors
owned_classes = [
- Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite,
+ Status, StatusPin, MediaAttachment, Report, Tombstone, Favourite,
Follow, FollowRequest, Block, Mute, AccountIdentityProof,
- AccountModerationNote, AccountPin, AccountStat, ListAccount,
- PollVote, Mention, AccountDeletionRequest, AccountNote, FollowRecommendationSuppression
+ AccountModerationNote, AccountPin, AccountFollowerStatistic,
+ AccountFollowingStatistic, AccountStatusStatistic, ListAccount,
+ Mention, AccountDeletionRequest, AccountNote, FollowRecommendationSuppression
]
owned_classes.each do |klass|
klass.where(account_id: other_account.id).find_each do |record|
record.update_attribute(:account_id, id)
- rescue ActiveRecord::RecordNotUnique
+ rescue
next
end
end
@@ -45,7 +46,7 @@
# Some follow relationships have moved, so the cache is stale
Rails.cache.delete_matched("followers_hash:#{id}:*")
- Rails.cache.delete_matched("relationships:#{id}:*")
- Rails.cache.delete_matched("relationships:*:#{id}")
+ Rails.cache.delete_matched("relationships/#{id}/*")
+ Rails.cache.delete_matched("relationships/*/#{id}")
end
end
diff -ru truth-old/opensource/app/models/concerns/attachmentable.rb truth-new/opensource/app/models/concerns/attachmentable.rb
--- truth-old/opensource/app/models/concerns/attachmentable.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/attachmentable.rb 2024-04-01 14:59:13
@@ -20,10 +20,10 @@
).freeze
included do
- before_post_process :obfuscate_file_name
- before_post_process :set_file_extensions
- before_post_process :check_image_dimensions
- before_post_process :set_file_content_type
+ before_validation :obfuscate_file_name
+ before_validation :set_file_extensions
+ before_validation :check_image_dimensions
+ before_validation :set_file_content_type
end
private
Only in truth-new/opensource/app/models/concerns: group_counters.rb
Only in truth-new/opensource/app/models/concerns: group_relationship_cacheable.rb
diff -ru truth-old/opensource/app/models/concerns/paginable.rb truth-new/opensource/app/models/concerns/paginable.rb
--- truth-old/opensource/app/models/concerns/paginable.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/paginable.rb 2024-04-01 14:59:13
@@ -28,5 +28,11 @@
paginate_by_max_id(limit, options[:max_id], options[:since_id]).to_a
end
end
+
+ def self.paginate_by_limit_offset(limit, params)
+ query = limit(limit)
+ query = query.offset(params[:offset]) if params[:offset].present?
+ query
+ end
end
end
Only in truth-new/opensource/app/models/concerns: queriable.rb
diff -ru truth-old/opensource/app/models/concerns/rate_limitable.rb truth-new/opensource/app/models/concerns/rate_limitable.rb
--- truth-old/opensource/app/models/concerns/rate_limitable.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/rate_limitable.rb 2023-05-05 13:42:02
@@ -14,7 +14,11 @@
def rate_limiter(by, options = {})
return @rate_limiter if defined?(@rate_limiter)
- @rate_limiter = RateLimiter.new(by, options)
+ @rate_limiter = if by.is_a?(Account) && by.trust_level == Account::TRUST_LEVELS[:hostile]
+ HostileRateLimiter.new(by, options)
+ else
+ RateLimiter.new(by, options)
+ end
end
class_methods do
diff -ru truth-old/opensource/app/models/concerns/relationship_cacheable.rb truth-new/opensource/app/models/concerns/relationship_cacheable.rb
--- truth-old/opensource/app/models/concerns/relationship_cacheable.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/relationship_cacheable.rb 2024-04-01 14:59:13
@@ -10,7 +10,7 @@
private
def remove_relationship_cache
- Rails.cache.delete("relationship:#{account_id}:#{target_account_id}")
- Rails.cache.delete("relationship:#{target_account_id}:#{account_id}")
+ Rails.cache.delete("relationship/#{account_id}/#{target_account_id}")
+ Rails.cache.delete("relationship/#{target_account_id}/#{account_id}")
end
end
diff -ru truth-old/opensource/app/models/concerns/remotable.rb truth-new/opensource/app/models/concerns/remotable.rb
--- truth-old/opensource/app/models/concerns/remotable.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/remotable.rb 2024-04-01 14:59:13
@@ -28,11 +28,11 @@
public_send("#{attachment_name}=", ResponseWithLimit.new(response, limit))
end
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e
- Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
+ Rails.logger.info "Error fetching remote #{attachment_name}: #{e}"
public_send("#{attachment_name}=", nil) if public_send("#{attachment_name}_file_name").present?
raise e unless suppress_errors && !should_raise_error(parsed_url.host)
rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
- Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
+ Rails.logger.info "Error fetching remote #{attachment_name}: #{e}"
public_send("#{attachment_name}=", nil) if public_send("#{attachment_name}_file_name").present?
end
diff -ru truth-old/opensource/app/models/concerns/status_threading_concern.rb truth-new/opensource/app/models/concerns/status_threading_concern.rb
--- truth-old/opensource/app/models/concerns/status_threading_concern.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/concerns/status_threading_concern.rb 2024-04-01 14:59:13
@@ -2,151 +2,16 @@
module StatusThreadingConcern
extend ActiveSupport::Concern
- include Redisable
def ancestors(limit, account = nil)
- find_statuses_from_tree_path(ancestor_ids(limit), account)
+ StatusRepliesV1.new(self).ancestors(limit, account)
end
def descendants(limit, account = nil, offset = 0, max_child_id = nil, since_child_id = nil, depth = nil)
- find_statuses_from_tree_path(descendant_ids(limit, offset, max_child_id, since_child_id, depth), account, promote: true)
+ StatusRepliesV1.new(self).descendants(limit, account, offset, max_child_id, since_child_id, depth)
end
def self_replies(limit)
account.statuses.where(in_reply_to_id: id, visibility: [:public, :unlisted]).reorder(id: :asc).limit(limit)
- end
-
- private
-
- def ancestor_ids(limit)
- key = "ancestors:#{id}"
- ancestors = Rails.cache.fetch(key)
-
- if ancestors.nil? || ancestors[:limit] < limit
- ids = ancestor_statuses(limit).pluck(:id).reverse!
- Rails.cache.write key, limit: limit, ids: ids
- ids
- else
- ancestors[:ids].last(limit)
- end
- end
-
- def ancestor_statuses(limit)
- Status.find_by_sql([<<-SQL.squish, id: in_reply_to_id, limit: limit])
- WITH RECURSIVE search_tree(id, in_reply_to_id, path)
- AS (
- SELECT id, in_reply_to_id, ARRAY[id]
- FROM statuses
- WHERE id = :id
- UNION ALL
- SELECT statuses.id, statuses.in_reply_to_id, path || statuses.id
- FROM search_tree
- JOIN statuses ON statuses.id = search_tree.in_reply_to_id
- WHERE NOT statuses.id = ANY(path)
- )
- SELECT id
- FROM search_tree
- ORDER BY path
- LIMIT :limit
- SQL
- end
-
- def descendant_ids(limit, offset, max_child_id, since_child_id, depth)
- key = "descendants:#{conversation_id}"
- field = "#{id}:#{limit}:#{offset}"
- if (cached_descendants = get_descendants_from_cache(key, field))
- cached_descendants
- else
- ids = descendant_statuses(limit, offset, max_child_id, since_child_id, depth).pluck(:id)
- redis.hset(key, field, ids.to_json)
- redis.expire(key, 1.hour.seconds)
- ids
- end
- end
-
- def descendant_statuses(limit, offset, max_child_id, since_child_id, depth)
- # use limit + 1 and depth + 1 because 'self' is included
- depth += 1 if depth.present?
- offset += 1 if offset.present?
-
- descendants_with_self = Status.find_by_sql([<<-SQL.squish, id: id, limit: limit, offset: offset, max_child_id: max_child_id, since_child_id: since_child_id, depth: depth])
- WITH RECURSIVE search_tree(id, path)
- AS (
- SELECT id, ARRAY[id]
- FROM statuses
- WHERE id = :id AND COALESCE(id < :max_child_id, TRUE) AND COALESCE(id > :since_child_id, TRUE)
- UNION ALL
- SELECT statuses.id, path || statuses.id
- FROM search_tree
- JOIN statuses ON statuses.in_reply_to_id = search_tree.id
- WHERE COALESCE(array_length(path, 1) < :depth, TRUE) AND NOT statuses.id = ANY(path)
- )
- SELECT id
- FROM search_tree
- ORDER BY path
- OFFSET :offset
- LIMIT :limit
- SQL
-
- descendants_with_self
- end
-
- def find_statuses_from_tree_path(ids, account, promote: false)
- statuses = Status.with_accounts(ids).to_a
- account_ids = statuses.map(&:account_id).uniq
- domains = statuses.filter_map(&:account_domain).uniq
- relations = relations_map_for_account(account, account_ids, domains)
-
- statuses.reject! { |status| StatusFilter.new(status, account, relations).filtered? }
-
- # Order ancestors/descendants by tree path
- statuses.sort_by! { |status| ids.index(status.id) }
-
- # Bring self-replies to the top
- if promote
- promote_by!(statuses) { |status| status.in_reply_to_account_id == status.account_id }
- else
- statuses
- end
- end
-
- def promote_by!(arr)
- insert_at = arr.find_index { |item| !yield(item) }
-
- return arr if insert_at.nil?
-
- arr.each_with_index do |item, index|
- next if index <= insert_at || !yield(item)
-
- arr.insert(insert_at, arr.delete_at(index))
- insert_at += 1
- end
-
- arr
- end
-
- def relations_map_for_account(account, account_ids, domains)
- return {} if account.nil?
-
- {
- blocking: Account.blocking_map(account_ids, account.id),
- blocked_by: Account.blocked_by_map(account_ids, account.id),
- muting: Account.muting_map(account_ids, account.id),
- following: Account.following_map(account_ids, account.id),
- domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
- }
- end
-
- def get_descendants_from_cache(key, field)
- cached_descendants_raw = redis.hget(key, field)
-
- return if cached_descendants_raw.nil?
-
- begin
- parsed = JSON.parse(cached_descendants_raw)
- parsed.is_a?(Array) ? parsed : false
- rescue JSON::ParserError
- false
- end
end
end
Only in truth-new/opensource/app/models/concerns: status_threading_concern_v2.rb
Only in truth-new/opensource/app/models: configuration
Only in truth-new/opensource/app/models: country.rb
Only in truth-new/opensource/app/models: current.rb
diff -ru truth-old/opensource/app/models/device.rb truth-new/opensource/app/models/device.rb
--- truth-old/opensource/app/models/device.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/device.rb 2024-04-01 14:59:13
@@ -15,7 +15,7 @@
#
class Device < ApplicationRecord
- belongs_to :access_token, class_name: 'Doorkeeper::AccessToken'
+ belongs_to :access_token, class_name: 'OauthAccessToken'
belongs_to :account
has_many :one_time_keys, dependent: :destroy, inverse_of: :device
Only in truth-new/opensource/app/models: device_verification.rb
Only in truth-new/opensource/app/models: device_verification_chat_message.rb
Only in truth-new/opensource/app/models: device_verification_favourite.rb
Only in truth-new/opensource/app/models: device_verification_registration.rb
Only in truth-new/opensource/app/models: device_verification_status.rb
Only in truth-new/opensource/app/models: device_verification_user.rb
diff -ru truth-old/opensource/app/models/email_domain_block.rb truth-new/opensource/app/models/email_domain_block.rb
--- truth-old/opensource/app/models/email_domain_block.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/email_domain_block.rb 2024-04-01 14:59:13
@@ -3,11 +3,12 @@
#
# Table name: email_domain_blocks
#
-# id :bigint(8) not null, primary key
-# domain :string default(""), not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# parent_id :bigint(8)
+# id :bigint(8) not null, primary key
+# domain :string default(""), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# parent_id :bigint(8)
+# disposable :boolean
#
class EmailDomainBlock < ApplicationRecord
@@ -17,6 +18,7 @@
has_many :children, class_name: 'EmailDomainBlock', foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
validates :domain, presence: true, uniqueness: true, domain: true
+ validate :real_domain
def with_dns_records=(val)
@with_dns_records = ActiveModel::Type::Boolean.new.cast(val)
@@ -28,6 +30,16 @@
alias with_dns_records with_dns_records?
+ def with_domain_validation=(val)
+ @with_domain_validation = ActiveModel::Type::Boolean.new.cast(val)
+ end
+
+ def with_domain_validation?
+ @with_domain_validation
+ end
+
+ alias with_domain_validation with_domain_validation?
+
def self.block?(email)
_, domain = email.split('@', 2)
@@ -40,5 +52,19 @@
end
where(domain: domain).exists?
+ end
+
+ private
+
+ def real_domain
+ if @with_domain_validation && domain.present? && !domain.nil?
+ begin
+ if Addressable::URI.parse("http://#{domain}").domain.nil?
+ errors.add(:domain, 'is invalid domain')
+ end
+ rescue Addressable::URI::InvalidURIError
+ errors.add(:domain, 'is invalid domain')
+ end
+ end
end
end
Only in truth-new/opensource/app/models: external_ad.rb
diff -ru truth-old/opensource/app/models/favourite.rb truth-new/opensource/app/models/favourite.rb
--- truth-old/opensource/app/models/favourite.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/favourite.rb 2024-04-01 14:59:13
@@ -12,8 +12,9 @@
class Favourite < ApplicationRecord
include Paginable
+ extend Queriable
- update_index('statuses#status', :status)
+ update_index('statuses', :status)
belongs_to :account, inverse_of: :favourites
belongs_to :status, inverse_of: :favourites
@@ -24,19 +25,5 @@
before_validation do
self.status = status.reblog if status&.reblog?
- end
-
- after_create :increment_cache_counters
- after_destroy :decrement_cache_counters
-
- private
-
- def increment_cache_counters
- status&.increment_count!(:favourites_count)
- end
-
- def decrement_cache_counters
- return if association(:status).loaded? && status.marked_for_destruction?
- status&.decrement_count!(:favourites_count)
end
end
diff -ru truth-old/opensource/app/models/feed.rb truth-new/opensource/app/models/feed.rb
--- truth-old/opensource/app/models/feed.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/feed.rb 2024-04-01 14:59:13
@@ -17,18 +17,28 @@
@fanout_key = feed_key(@type, @account.id)
status_ids = !@whale_following.empty? && @type == :home ? get_with_whales : get_fanout_only
- Status.where(id: status_ids).cache_ids
+
+ Status.
+ where(id: status_ids).
+ where.not(visibility: "self").
+ or(Status.where(id: status_ids).where(account_id: @account.id)).
+ cache_ids
end
+ def clear!
+ key_to_clear = feed_key(@type, @account.id)
+ redis_timelines.del(key_to_clear)
+ end
+
protected
def get_fanout_only
@max_id = '+inf' if @max_id.blank?
if @min_id.blank?
@since_id = '-inf' if @since_id.blank?
- redis_timelines.zrevrangebyscore(@fanout_key, "(#{@max_id}", "(#{@since_id}", limit: [0, @limit], with_scores: true).map(&:first).map(&:to_i)
+ FeedManager.instance.status_ids_to_plain_numbers(redis_timelines.zrevrangebyscore(@fanout_key, "(#{@max_id}", "(#{@since_id}", limit: [0, @limit], with_scores: true).map(&:first)).map(&:to_i)
else
- redis_timelines.zrangebyscore(@fanout_key, "(#{@min_id}", "(#{@max_id}", limit: [0, @limit], with_scores: true).map(&:first).map(&:to_i)
+ FeedManager.instance.status_ids_to_plain_numbers(redis_timelines.zrangebyscore(@fanout_key, "(#{@min_id}", "(#{@max_id}", limit: [0, @limit], with_scores: true).map(&:first)).map(&:to_i)
end
end
@@ -63,7 +73,7 @@
pipeline.zrange(feed_key(:whale, account_id), '0', '-1')
end
end
- subsets.flatten.map(&:to_i)
+ FeedManager.instance.status_ids_to_plain_numbers(subsets.flatten).map(&:to_i)
end
def whale_following
Only in truth-new/opensource/app/models: feeds
diff -ru truth-old/opensource/app/models/follow.rb truth-new/opensource/app/models/follow.rb
--- truth-old/opensource/app/models/follow.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/follow.rb 2024-04-01 14:59:13
@@ -40,10 +40,8 @@
end
before_validation :set_uri, only: :create
- after_create :increment_cache_counters
after_create :invalidate_hash_cache
after_destroy :remove_endorsements
- after_destroy :decrement_cache_counters
after_destroy :invalidate_hash_cache
private
@@ -54,16 +52,6 @@
def remove_endorsements
AccountPin.where(target_account_id: target_account_id, account_id: account_id).delete_all
- end
-
- def increment_cache_counters
- account&.increment_count!(:following_count)
- target_account&.increment_count!(:followers_count)
- end
-
- def decrement_cache_counters
- account&.decrement_count!(:following_count)
- target_account&.decrement_count!(:followers_count)
end
def invalidate_hash_cache
Only in truth-new/opensource/app/models: follow_delete.rb
Only in truth-new/opensource/app/models: follow_suggestion.rb
diff -ru truth-old/opensource/app/models/form/account_batch.rb truth-new/opensource/app/models/form/account_batch.rb
--- truth-old/opensource/app/models/form/account_batch.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/form/account_batch.rb 2024-04-01 14:59:13
@@ -81,7 +81,16 @@
records = accounts.includes(:user)
records.each { |account| authorize(account.user, :reject?) }
- .each { |account| DeleteAccountService.new.call(account, reserve_email: false, reserve_username: false) }
+ .each do |account|
+ DeleteAccountService.new.call(
+ account,
+ current_account.id,
+ deletion_type: 'account_batch_reject',
+ reserve_email: false,
+ reserve_username: false,
+ skip_activitypub: true,
+ )
+ end
end
def suppress_follow_recommendation!
diff -ru truth-old/opensource/app/models/form/status_batch.rb truth-new/opensource/app/models/form/status_batch.rb
--- truth-old/opensource/app/models/form/status_batch.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/form/status_batch.rb 2024-04-01 14:59:13
@@ -34,8 +34,9 @@
def delete_statuses
Status.where(id: status_ids).reorder(nil).find_each do |status|
+ status.reblogs.update_all(deleted_at: Time.current, deleted_by_id: current_account.id)
status.update!(deleted_at: Time.current, deleted_by_id: current_account.id)
- RemovalWorker.perform_async(status.id, immediate: true)
+ RemovalWorker.perform_async(status.id, immediate: false)
Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true)
log_action :destroy, status
end
Only in truth-new/opensource/app/models: group.rb
Only in truth-new/opensource/app/models: group_account_block.rb
Only in truth-new/opensource/app/models: group_avatar.rb
Only in truth-new/opensource/app/models: group_deletion_request.rb
Only in truth-new/opensource/app/models: group_feed.rb
Only in truth-new/opensource/app/models: group_filter.rb
Only in truth-new/opensource/app/models: group_header.rb
Only in truth-new/opensource/app/models: group_membership.rb
Only in truth-new/opensource/app/models: group_membership_request.rb
Only in truth-new/opensource/app/models: group_mute.rb
Only in truth-new/opensource/app/models: group_stat.rb
Only in truth-new/opensource/app/models: group_suggestion.rb
Only in truth-new/opensource/app/models: group_suggestion_delete.rb
Only in truth-new/opensource/app/models: group_tag.rb
Only in truth-new/opensource/app/models: groups_carousel.rb
Only in truth-new/opensource/app/models: groups_feed.rb
Only in truth-new/opensource/app/models: link.rb
Only in truth-new/opensource/app/models: logs
diff -ru truth-old/opensource/app/models/media_attachment.rb truth-new/opensource/app/models/media_attachment.rb
--- truth-old/opensource/app/models/media_attachment.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/media_attachment.rb 2024-04-12 09:09:12
@@ -27,12 +27,13 @@
# thumbnail_updated_at :datetime
# thumbnail_remote_url :string
# external_video_id :string
+# file_s3_host :string(64)
#
class MediaAttachment < ApplicationRecord
self.inheritance_column = nil
- enum type: [:image, :gifv, :video, :unknown, :audio]
+ enum type: [:image, :gifv, :video, :unknown, :audio, :tv]
enum processing: [:queued, :in_progress, :complete, :failed], _prefix: true
MAX_DESCRIPTION_LENGTH = 1_500
@@ -48,10 +49,10 @@
small
).freeze
- IMAGE_MIME_TYPES = %w(image/jpeg image/png image/gif).freeze
- VIDEO_MIME_TYPES = %w(video/webm video/mp4 video/quicktime video/ogg).freeze
- VIDEO_CONVERTIBLE_MIME_TYPES = %w(video/webm video/quicktime).freeze
- AUDIO_MIME_TYPES = %w(audio/wave audio/wav audio/x-wav audio/x-pn-wave audio/ogg audio/mpeg audio/mp3 audio/webm audio/flac audio/aac audio/m4a audio/x-m4a audio/mp4 audio/3gpp video/x-ms-asf).freeze
+ IMAGE_MIME_TYPES = %w(image/jpeg image/png image/gif).freeze
+ VIDEO_MIME_TYPES = %w(video/webm video/mp4 video/quicktime video/ogg application/octet-stream).freeze
+ VIDEO_CONVERTIBLE_MIME_TYPES = %w(video/webm video/quicktime application/octet-stream).freeze
+ AUDIO_MIME_TYPES = %w(audio/wave audio/wav audio/x-wav audio/x-pn-wave audio/ogg audio/mpeg audio/mp3 audio/webm audio/flac audio/aac audio/m4a audio/x-m4a audio/mp4 audio/3gpp video/x-ms-asf).freeze
BLURHASH_OPTIONS = {
x_comp: 4,
@@ -152,13 +153,14 @@
}.freeze
IMAGE_LIMIT = 10.megabytes
- VIDEO_LIMIT = 40.megabytes
+ VIDEO_LIMIT = 450.megabytes
- MAX_VIDEO_MATRIX_LIMIT = 2_304_000 # 1920x1200px
- MAX_VIDEO_FRAME_RATE = 60
+ MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840 x 2160
+ MAX_VIDEO_FRAME_RATE = 60
+ MAX_VIDEO_DURATION_LIMIT = 900 # 900 seconds
- belongs_to :account, inverse_of: :media_attachments, optional: true
- belongs_to :status, inverse_of: :media_attachments, optional: true
+ belongs_to :account, inverse_of: :media_attachments, optional: true
+ belongs_to :status, inverse_of: :media_attachments, optional: true
belongs_to :scheduled_status, inverse_of: :media_attachments, optional: true
has_many :moderation_records
@@ -169,13 +171,14 @@
convert_options: GLOBAL_CONVERT_OPTIONS
before_file_post_process :set_type_and_extension
- before_file_post_process :do_not_process_video_files
+ before_file_post_process :check_video_dimensions
validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES
- validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :larger_media_format?
- validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :larger_media_format?
+ validates_attachment_size :file, less_than: ->(m) { m.larger_media_format? ? VIDEO_LIMIT : IMAGE_LIMIT }
remotable_attachment :file, VIDEO_LIMIT, suppress_errors: false, download_on_assign: false, attribute_name: :remote_url
+ remotable_attachment :file, IMAGE_LIMIT
+
has_attached_file :thumbnail,
styles: THUMBNAIL_STYLES,
processors: [:lazy_thumbnail, :blurhash_transcoder, :color_extractor],
@@ -183,7 +186,7 @@
validates_attachment_content_type :thumbnail, content_type: IMAGE_MIME_TYPES
validates_attachment_size :thumbnail, less_than: IMAGE_LIMIT
- remotable_attachment :thumbnail, IMAGE_LIMIT, suppress_errors: true, download_on_assign: false
+ remotable_attachment :thumbnail, IMAGE_LIMIT, suppress_errors: true
include Attachmentable
@@ -193,7 +196,7 @@
validates :thumbnail, absence: true, if: -> { local? && !audio_or_video? }
scope :attached, -> { where.not(status_id: nil).or(where.not(scheduled_status_id: nil)) }
- scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) }
+ scope :unattached, -> { select('*').from('(select a.* from media_attachments a where a.status_id is null and a.scheduled_status_id is null and not exists (select 1 from chats.message_media_attachments m where m.media_attachment_id = a.id)) as media_attachments') }
scope :local, -> { where(remote_url: '') }
scope :remote, -> { where.not(remote_url: '') }
scope :cached, -> { remote.where.not(file_file_name: nil) }
@@ -255,10 +258,13 @@
end
after_commit :enqueue_processing, on: :create
+ after_commit :publish_event, if: -> { saved_change_to_processing?(to: 'complete') }
after_commit :reset_parent_cache, on: :update
before_create :prepare_description, unless: :local?
before_create :set_shortcode
+ before_save :set_file_s3_host
+
before_create :set_processing
after_post_process :set_meta
@@ -275,7 +281,9 @@
private
def file_styles(attachment)
- if attachment.instance.file_content_type == 'image/gif' || VIDEO_CONVERTIBLE_MIME_TYPES.include?(attachment.instance.file_content_type)
+ # only convert gifs to video. Don't convert hevc and webm videos
+ # Let those fall to the video_styles where they will just be passthrough encoded
+ if attachment.instance.file_content_type == 'image/gif' # || VIDEO_CONVERTIBLE_MIME_TYPES.include?(attachment.instance.file_content_type)
VIDEO_CONVERTED_STYLES
elsif IMAGE_MIME_TYPES.include?(attachment.instance.file_content_type)
IMAGE_STYLES
@@ -312,6 +320,10 @@
end
end
+ def set_file_s3_host
+ self.file_s3_host = Paperclip::Attachment.default_options[:s3_host_name]
+ end
+
def prepare_description
self.description = description.strip[0...MAX_DESCRIPTION_LENGTH] unless description.nil?
end
@@ -354,6 +366,10 @@
file.instance_write :meta, populate_meta
end
+ def publish_event
+ EventProvider::EventProvider.new('asset.created', ::AssetCreatedEvent, self).call
+ end
+
def populate_meta
meta = (file.instance_read(:meta) || {}).with_indifferent_access.slice(*META_KEYS)
@@ -402,10 +418,10 @@
def enqueue_processing
if video?
- self.processing = :complete
- self.save
- else
- PostProcessMediaWorker.perform_async(id) if delay_processing?
+ self.processing = :queued
+ save
+ elsif delay_processing?
+ PostProcessMediaWorker.perform_async(id)
end
end
diff -ru truth-old/opensource/app/models/notification.rb truth-new/opensource/app/models/notification.rb
--- truth-old/opensource/app/models/notification.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/notification.rb 2024-04-01 14:59:13
@@ -15,6 +15,8 @@
#
class Notification < ApplicationRecord
+ self.primary_key = :id
+
self.inheritance_column = nil
include Paginable
@@ -27,7 +29,7 @@
'FollowRequest' => :follow_request,
'Favourite' => :favourite,
'Poll' => :poll,
- 'User' => :user_approved,
+ 'User' => :user_approved
}.freeze
TYPES = %i(
@@ -41,10 +43,27 @@
poll
user_approved
verify_sms_prompt
+ chat
+ chat_message
+ chat_message_deleted
mention_group
reblog_group
follow_group
favourite_group
+ group_favourite
+ group_favourite_group
+ group_reblog
+ group_reblog_group
+ group_mention
+ group_mention_group
+ group_approval
+ group_delete
+ group_role
+ group_request
+ group_request_group
+ group_accepted
+ group_promoted
+ group_demoted
).freeze
TARGET_STATUS_INCLUDES_BY_TYPE = {
@@ -59,6 +78,12 @@
poll: [poll: :status],
user_approved: :user,
verify_sms_prompt: :user,
+ group_favourite: [favourite: :status],
+ group_favourite_group: [favourite: :status],
+ group_reblog: [status: :reblog],
+ group_reblog_group: [status: :reblog],
+ group_mention: [mention: :status],
+ group_mention_group: [mention: :status]
}.freeze
attr_accessor :passed_from_account
@@ -75,41 +100,61 @@
belongs_to :favourite, foreign_key: 'activity_id', optional: true
belongs_to :poll, foreign_key: 'activity_id', optional: true
belongs_to :user, foreign_key: 'activity_id', optional: true
+ belongs_to :group, foreign_key: 'activity_id', optional: true
+ belongs_to :group_membership_request, foreign_key: 'activity_id', optional: true
validates :type, inclusion: { in: TYPES }
scope :without_suspended, -> { joins(:from_account).merge(Account.without_suspended) }
- scope :browserable, ->(exclude_types = [], account_id = nil) {
- types = TYPES - exclude_types.map(&:to_sym)
-
- if account_id.nil?
- where(type: types)
- else
- where(type: types, from_account_id: account_id)
- end
- }
-
def type
@type ||= (super || LEGACY_TYPE_CLASS_MAP[activity_type]).to_sym
end
def target_status
case type
- when :status, :favourite_group, :mention_group, :reblog_group
+ when :status, :favourite_group, :mention_group, :reblog_group, :group_favourite_group, :group_mention_group, :group_reblog_group
status
- when :reblog
+ when :reblog, :group_reblog
status&.reblog
- when :favourite
+ when :favourite, :group_favourite
favourite&.status
- when :mention
+ when :mention, :group_mention
mention&.status
when :poll
poll&.status
end
end
+ def target_group
+ case type
+ when :group_approval, :group_promoted, :group_demoted, :group_delete
+ group
+ when :group_request
+ group_membership_request
+ end
+ end
+
class << self
+ def browserable(types: [], exclude_types: [], from_account_id: nil)
+ requested_types = begin
+ if types.empty?
+ TYPES
+ else
+ types.map(&:to_sym) & TYPES
+ end
+ end
+
+ exclude_types_with_groups = exclude_types.clone
+ exclude_types.each { |n| exclude_types_with_groups << "#{n}_group"}
+ requested_types -= exclude_types_with_groups.map(&:to_sym)
+
+ all.tap do |scope|
+ scope.merge!(where(from_account_id: from_account_id)) if from_account_id.present?
+ scope.merge!(where(type: requested_types)) unless requested_types.size == TYPES.size
+ end
+ end
+
def preload_cache_collection_target_statuses(notifications, &_block)
notifications.group_by(&:type).each do |type, grouped_notifications|
associations = TARGET_STATUS_INCLUDES_BY_TYPE[type]
@@ -130,13 +175,13 @@
cached_status = cached_statuses_by_id[notification.target_status.id]
case notification.type
- when :status, :favourite_group, :mention_group, :reblog_group
+ when :status, :favourite_group, :mention_group, :reblog_group, :group_favourite_group, :group_mention_group, :group_reblog_group
notification.status = cached_status
- when :reblog
+ when :reblog, :group_reblog
notification.status.reblog = cached_status
- when :favourite
+ when :favourite, :group_favourite
notification.favourite.status = cached_status
- when :mention
+ when :mention, :group_mention
notification.mention.status = cached_status
when :poll
notification.poll.status = cached_status
@@ -145,6 +190,10 @@
notifications
end
+
+ def exclude_self_statuses(notifications)
+ notifications.delete_if { |n| n.target_status&.visibility == 'self' }
+ end
end
after_initialize :set_from_account
@@ -166,7 +215,22 @@
self.from_account_id = activity&.users&.first&.account_id
when 'User'
self.from_account_id = activity&.account_id
+ when 'ChatMessage'
+ self.from_account_id = activity&.created_by_account_id
+ when 'Group'
+ self.from_account_id = set_by_notification_type
+ when 'GroupMembershipRequest'
+ self.from_account_id = activity&.account&.id
end
+ end
+ end
+
+ def set_by_notification_type
+ case type
+ when :group_delete
+ activity&.owner_account&.id
+ else
+ activity&.owner_account&.id
end
end
end
Only in truth-new/opensource/app/models: notifications_marketing.rb
Only in truth-new/opensource/app/models: notifications_marketing_analytic.rb
Only in truth-new/opensource/app/models: oauth_access_token.rb
Only in truth-new/opensource/app/models: oauth_access_tokens
Only in truth-new/opensource/app/models: one_time_challenge.rb
Only in truth-new/opensource/app/models: password_history.rb
Only in truth-new/opensource/app/models: policy.rb
diff -ru truth-old/opensource/app/models/poll.rb truth-new/opensource/app/models/poll.rb
--- truth-old/opensource/app/models/poll.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/poll.rb 2024-04-01 14:59:13
@@ -1,115 +1,82 @@
# frozen_string_literal: true
# == Schema Information
#
-# Table name: polls
+# Table name: polls.polls
#
-# id :bigint(8) not null, primary key
-# account_id :bigint(8)
-# status_id :bigint(8)
-# expires_at :datetime
-# options :string default([]), not null, is an Array
-# cached_tallies :bigint(8) default([]), not null, is an Array
-# multiple :boolean default(FALSE), not null
-# hide_totals :boolean default(FALSE), not null
-# votes_count :bigint(8) default(0), not null
-# last_fetched_at :datetime
-# created_at :datetime not null
-# updated_at :datetime not null
-# lock_version :integer default(0), not null
-# voters_count :bigint(8)
+# poll_id :integer not null, primary key
+# expires_at :datetime not null
+# multiple_choice :boolean default(FALSE), not null
#
class Poll < ApplicationRecord
include Expireable
- belongs_to :account
- belongs_to :status
+ self.table_name = 'polls.polls'
+ self.primary_key = :poll_id
- has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :delete_all
+ has_and_belongs_to_many :statuses, join_table: 'polls.status_polls'
+ has_many :options, class_name: 'PollOption', inverse_of: :poll, dependent: :delete_all
+ has_many :votes, class_name: 'PollVote', inverse_of: :poll
has_many :notifications, as: :activity, dependent: :destroy
+ has_one :statistic_polls, class_name: 'StatisticPoll', inverse_of: :poll
+ has_one :status_polls, class_name: 'StatusPolls'
+ has_one :status, through: :status_polls, source: :status
+ has_one :account, through: :status
- validates :options, presence: true
- validates :expires_at, presence: true, if: :local?
- validates_with PollValidator, on: :create, if: :local?
+ accepts_nested_attributes_for :options
- scope :attached, -> { where.not(status_id: nil) }
- scope :unattached, -> { where(status_id: nil) }
+ validates :expires_at, presence: true
+ validates_with PollValidator, on: :create
- before_validation :prepare_options, if: :local?
- before_validation :prepare_votes_count
+ alias_attribute :multiple, :multiple_choice
- after_initialize :prepare_cached_tallies
-
- after_commit :reset_parent_cache, on: :update
-
def loaded_options
- options.map.with_index { |title, key| Option.new(self, key.to_s, title, show_totals_now? ? cached_tallies[key] : nil) }
+ loaded_poll_options
end
- def possibly_stale?
- remote? && last_fetched_before_expiration? && time_passed_since_last_fetch?
+ def loaded_poll_options
+ options = PollOption
+ .joins('LEFT JOIN statistics.poll_options using("poll_id", "option_number")')
+ .where(options: { poll_id: id })
+ .order(option_number: :asc)
+ .select('polls.options.*, statistics.poll_options.votes votes_count')
+ options.map { |option| { title: option.text, votes_count: option.has_attribute?(:votes_count) && option.votes_count ? option.votes_count : 0 } }
end
def voted?(account)
- account.id == account_id || votes.where(account: account).exists?
+ PollVote.where(poll_id: id, account: account).exists?
end
def own_votes(account)
- votes.where(account: account).pluck(:choice)
+ PollVote.where(poll_id: id, account: account).pluck(:option_number) || []
end
delegate :local?, to: :account
+ def local?
+ true
+ end
+
def remote?
- !local?
+ false
end
def emojis
- @emojis ||= CustomEmoji.from_text(options.join(' '), account.domain)
+ []
end
- class Option < ActiveModelSerializers::Model
- attributes :id, :title, :votes_count, :poll
-
- def initialize(poll, id, title, votes_count)
- super(
- poll: poll,
- id: id,
- title: title,
- votes_count: votes_count,
- )
- end
+ def votes_count
+ statistic_polls&.votes || 0
end
- private
-
- def prepare_cached_tallies
- self.cached_tallies = options.map { 0 } if cached_tallies.empty?
+ def voters_count
+ statistic_polls&.voters || 0
end
- def prepare_votes_count
- self.votes_count = cached_tallies.sum unless cached_tallies.empty?
+ def statistic_polls
+ super || build_statistic_polls
end
- def prepare_options
- self.options = options.map(&:strip).reject(&:blank?)
- end
-
- def reset_parent_cache
- return if status_id.nil?
- Rails.cache.delete("statuses/#{status_id}")
- end
-
- def last_fetched_before_expiration?
- last_fetched_at.nil? || expires_at.nil? || last_fetched_at < expires_at
- end
-
- def time_passed_since_last_fetch?
- last_fetched_at.nil? || last_fetched_at < 1.minute.ago
- end
-
- def show_totals_now?
- expired? || !hide_totals?
- end
+ delegate :id, to: :account, prefix: true
end
Only in truth-new/opensource/app/models: poll_option.rb
diff -ru truth-old/opensource/app/models/poll_vote.rb truth-new/opensource/app/models/poll_vote.rb
--- truth-old/opensource/app/models/poll_vote.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/poll_vote.rb 2024-04-01 14:59:13
@@ -1,39 +1,24 @@
# frozen_string_literal: true
# == Schema Information
#
-# Table name: poll_votes
+# Table name: polls.votes
#
-# id :bigint(8) not null, primary key
-# account_id :bigint(8)
-# poll_id :bigint(8)
-# choice :integer default(0), not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# uri :string
+# poll_id :integer not null, primary key
+# option_number :integer not null, primary key
+# account_id :bigint(8) not null, primary key
#
class PollVote < ApplicationRecord
+ self.table_name = 'polls.votes'
+ self.primary_keys = :poll_id, :option_number, :account_id
+
belongs_to :account
- belongs_to :poll, inverse_of: :votes
+ belongs_to :poll
- validates :choice, presence: true
+ validates :poll_id, :option_number, :account_id, presence: true
validates_with VoteValidator
- after_create_commit :increment_counter_cache
-
- delegate :local?, to: :account
-
def object_type
:vote
- end
-
- private
-
- def increment_counter_cache
- poll.cached_tallies[choice] = (poll.cached_tallies[choice] || 0) + 1
- poll.save
- rescue ActiveRecord::StaleObjectError
- poll.reload
- retry
end
end
diff -ru truth-old/opensource/app/models/preview_card.rb truth-new/opensource/app/models/preview_card.rb
--- truth-old/opensource/app/models/preview_card.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/preview_card.rb 2024-04-01 14:59:13
@@ -24,11 +24,12 @@
# embed_url :string default(""), not null
# image_storage_schema_version :integer
# blurhash :string
+# file_s3_host :string(64)
#
class PreviewCard < ApplicationRecord
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze
- LIMIT = 1.megabytes
+ LIMIT = 2_500.kilobytes
BLURHASH_OPTIONS = {
x_comp: 4,
@@ -50,9 +51,12 @@
validates_attachment_size :image, less_than: LIMIT
remotable_attachment :image, LIMIT
+ attribute :ad, :boolean, default: false
+
scope :cached, -> { where.not(image_file_name: [nil, '']) }
before_save :extract_dimensions, if: :link?
+ before_save :set_file_s3_host, if: -> { will_save_change_to_image_file_name? }
def local?
false
@@ -74,6 +78,7 @@
# rubocop:disable Naming/MethodParameterName
def image_styles(f)
+ return {} if f.instance.ad && f.instance.image_content_type == 'image/gif'
styles = {
original: {
geometry: '800x800>',
@@ -102,5 +107,10 @@
self.width = width
self.height = height
+ end
+
+
+ def set_file_s3_host
+ self.file_s3_host = Paperclip::Attachment.default_options[:s3_host_name]
end
end
Only in truth-new/opensource/app/models: procedure.rb
Only in truth-new/opensource/app/models: recommendations
Only in truth-new/opensource/app/models: region.rb
Only in truth-new/opensource/app/models: registration.rb
Only in truth-new/opensource/app/models: registration_one_time_challenge.rb
Only in truth-new/opensource/app/models: registration_webauthn_credential.rb
diff -ru truth-old/opensource/app/models/relationship_filter.rb truth-new/opensource/app/models/relationship_filter.rb
--- truth-old/opensource/app/models/relationship_filter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/relationship_filter.rb 2024-04-01 14:59:13
@@ -60,13 +60,13 @@
def relationship_scope(value)
case value
when 'following'
- account.following.eager_load(:account_stat).reorder(nil)
+ account.following.eager_load(:account_status).reorder(nil)
when 'followed_by'
- account.followers.eager_load(:account_stat).reorder(nil)
+ account.followers.eager_load(:account_status).reorder(nil)
when 'mutual'
- account.followers.eager_load(:account_stat).reorder(nil).merge(Account.where(id: account.following))
+ account.followers.eager_load(:account_status).reorder(nil).merge(Account.where(id: account.following))
when 'invited'
- Account.joins(user: :invite).merge(Invite.where(user: account.user)).eager_load(:account_stat).reorder(nil)
+ Account.joins(user: :invite).merge(Invite.where(user: account.user)).eager_load(:account_status).reorder(nil)
else
raise "Unknown relationship: #{value}"
end
@@ -112,7 +112,7 @@
def activity_scope(value)
case value
when 'dormant'
- AccountStat.where(last_status_at: nil).or(AccountStat.where(AccountStat.arel_table[:last_status_at].lt(1.month.ago)))
+ AccountStatusStatistic.where(last_status_at: nil).or(AccountStatusStatistic.where(AccountStatusStatistic.arel_table[:last_status_at].lt(1.month.ago)))
else
raise "Unknown activity: #{value}"
end
diff -ru truth-old/opensource/app/models/report.rb truth-new/opensource/app/models/report.rb
--- truth-old/opensource/app/models/report.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/report.rb 2024-04-01 14:59:13
@@ -3,7 +3,6 @@
#
# Table name: reports
#
-# id :bigint(8) not null, primary key
# status_ids :bigint(8) default([]), not null, is an Array
# comment :text default(""), not null
# action_taken :boolean default(FALSE), not null
@@ -11,11 +10,15 @@
# updated_at :datetime not null
# account_id :bigint(8) not null
# action_taken_by_account_id :bigint(8)
+# id :bigint(8) not null, primary key
# target_account_id :bigint(8) not null
# assigned_account_id :bigint(8)
# uri :string
# forwarded :boolean
# rule_ids :integer default([]), not null, is an Array
+# message_ids :bigint(8) default([]), is an Array
+# group_id :bigint(8)
+# external_ad_id :integer
#
class Report < ApplicationRecord
@@ -28,6 +31,7 @@
belongs_to :target_account, class_name: 'Account'
belongs_to :action_taken_by_account, class_name: 'Account', optional: true
belongs_to :assigned_account, class_name: 'Account', optional: true
+ belongs_to :external_ad, optional: true
has_many :notes, class_name: 'ReportNote', foreign_key: :report_id, inverse_of: :report, dependent: :destroy
@@ -76,7 +80,6 @@
target_account.update(trust_level: Account::TRUST_LEVELS[:trusted])
end
- RemovalWorker.push_bulk(Status.with_discarded.discarded.where(id: status_ids).pluck(:id)) { |status_id| [status_id, { immediate: true }] }
update!(action_taken: true, action_taken_by_account_id: acting_account.id)
end
diff -ru truth-old/opensource/app/models/rule.rb truth-new/opensource/app/models/rule.rb
--- truth-old/opensource/app/models/rule.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/rule.rb 2024-04-01 14:59:13
@@ -12,13 +12,14 @@
# updated_at :datetime not null
# rule_type :integer default("content")
# subtext :text default(""), not null
+# name :text default(""), not null
#
class Rule < ApplicationRecord
include Discard::Model
self.discard_column = :deleted_at
- enum rule_type: { content: 0, account: 1 }
+ enum rule_type: { content: 0, account: 1, rule_type_group: 2 }
validates :text, presence: true, length: { maximum: 300 }
diff -ru truth-old/opensource/app/models/search.rb truth-new/opensource/app/models/search.rb
--- truth-old/opensource/app/models/search.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/search.rb 2024-04-01 14:59:13
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class Search < ActiveModelSerializers::Model
- attributes :accounts, :statuses, :hashtags
+ attributes :accounts, :statuses, :hashtags, :groups
end
diff -ru truth-old/opensource/app/models/session_activation.rb truth-new/opensource/app/models/session_activation.rb
--- truth-old/opensource/app/models/session_activation.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/session_activation.rb 2024-04-01 14:59:13
@@ -16,7 +16,7 @@
class SessionActivation < ApplicationRecord
belongs_to :user, inverse_of: :session_activations
- belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', dependent: :destroy, optional: true
+ belongs_to :access_token, class_name: 'OauthAccessToken', dependent: :destroy, optional: true
belongs_to :web_push_subscription, class_name: 'Web::PushSubscription', dependent: :destroy, optional: true
delegate :token,
@@ -70,7 +70,7 @@
end
def assign_access_token
- self.access_token = Doorkeeper::AccessToken.create!(access_token_attributes)
+ self.access_token = OauthAccessToken.create!(access_token_attributes)
end
def access_token_attributes
diff -ru truth-old/opensource/app/models/site_upload.rb truth-new/opensource/app/models/site_upload.rb
--- truth-old/opensource/app/models/site_upload.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/site_upload.rb 2023-05-05 13:42:02
@@ -12,6 +12,7 @@
# meta :json
# created_at :datetime not null
# updated_at :datetime not null
+# file_s3_host :string(64)
#
class SiteUpload < ApplicationRecord
@@ -22,6 +23,7 @@
validates :var, presence: true, uniqueness: true
before_save :set_meta
+ before_save :set_file_s3_host
after_commit :clear_cache
def cache_key
@@ -41,5 +43,10 @@
def clear_cache
Rails.cache.delete(cache_key)
+ end
+
+
+ def set_file_s3_host
+ self.file_s3_host = Paperclip::Attachment.default_options[:s3_host_name]
end
end
Only in truth-new/opensource/app/models: statistic_poll.rb
Only in truth-new/opensource/app/models: statistic_poll_option.rb
diff -ru truth-old/opensource/app/models/status.rb truth-new/opensource/app/models/status.rb
--- truth-old/opensource/app/models/status.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/status.rb 2024-04-12 16:08:26
@@ -21,20 +21,25 @@
# account_id :bigint(8) not null
# application_id :bigint(8)
# in_reply_to_account_id :bigint(8)
-# poll_id :bigint(8)
-# deleted_at :datetime
# quote_id :bigint(8)
+# deleted_at :datetime
# deleted_by_id :bigint(8)
+# group_id :bigint(8)
+# group_timeline_visible :boolean default(FALSE)
+# has_poll :boolean default(FALSE), not null
#
class Status < ApplicationRecord
+
before_destroy :unlink_from_conversations
include Discard::Model
include Paginable
include Cacheable
include StatusThreadingConcern
+ include StatusThreadingConcernV2
include RateLimitable
+ extend LinksParserConcern
rate_limit by: :account, family: :statuses
@@ -43,22 +48,34 @@
# If `override_timestamps` is set at creation time, Snowflake ID creation
# will be based on current time instead of `created_at`
attr_accessor :override_timestamps
+ attr_accessor :interactive_ad, :statuses_count_before_filter
- update_index 'statuses#status', :proper, unless: -> { skip_indexing? }
+ attr_accessor :seen
- enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility
+ attribute :tv_program_status?, :boolean, default: false
+ # Used to bypass some validations if we know the operation was initiated from an admin
+ attr_accessor :performed_by_admin
+
+ # attr_accessor :tombstone
+ attribute :tombstone, :boolean, default: false
+
+ update_index 'statuses', :proper, unless: -> { skip_indexing? }
+
+ enum visibility: [:public, :unlisted, :private, :direct, :limited, :self, :group], _suffix: :visibility
+
belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
belongs_to :account, inverse_of: :statuses
belongs_to :in_reply_to_account, foreign_key: 'in_reply_to_account_id', class_name: 'Account', optional: true
belongs_to :conversation, optional: true
- belongs_to :preloadable_poll, class_name: 'Poll', foreign_key: 'poll_id', optional: true
- belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies, optional: true
+ belongs_to :thread, -> { with_discarded }, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies, optional: true
belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true
- belongs_to :quote, foreign_key: 'quote_id', class_name: 'Status', inverse_of: :quoted, optional: true
+ belongs_to :quote, -> { with_discarded }, foreign_key: 'quote_id', class_name: 'Status', inverse_of: :quoted, optional: true
+ belongs_to :group, inverse_of: :statuses, optional: true
+
has_many :favourites, inverse_of: :status, dependent: :destroy
has_many :bookmarks, inverse_of: :status, dependent: :destroy
has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy
@@ -68,25 +85,40 @@
has_many :media_attachments, dependent: :nullify
has_many :quoted, foreign_key: 'quote_id', class_name: 'Status', inverse_of: :quote, dependent: :nullify
has_many :moderation_records, dependent: :nullify
+ has_many :status_pins, inverse_of: :status, dependent: :destroy
+ has_many :moderation_results, dependent: :destroy, class_name: Statuses::ModerationResult.name
has_and_belongs_to_many :tags
has_and_belongs_to_many :preview_cards
+ has_and_belongs_to_many :links
has_one :notification, as: :activity, dependent: :destroy
- has_one :status_stat, inverse_of: :status
- has_one :poll, inverse_of: :status, dependent: :destroy
- has_one :trending, dependent: :destroy
+ has_one :status_favourite, class_name: StatusFavouriteStatistic.name, inverse_of: :status
+ has_one :status_reply, class_name: StatusReplyStatistic.name, inverse_of: :status
+ has_one :status_reblog, class_name: StatusReblogStatistic.name, inverse_of: :status
+ has_one :analysis, class_name: Statuses::Analysis.name
+ has_and_belongs_to_many :polls, inverse_of: :status, join_table: 'polls.status_polls'
+ accepts_nested_attributes_for :polls
+
+ has_one :status_polls, class_name: 'StatusPolls'
+ has_one :preloadable_poll, through: :status_polls, source: :poll
+
+ has_one :trending_status, class_name: 'TrendingStatus', dependent: :destroy
+ has_one :ad
+ has_one :tv_program_status
+ has_one :tv_program, through: :tv_program_status
+ has_one :tv_status
+
validates :uri, uniqueness: true, presence: true, unless: :local?
- validates :text, presence: true, unless: -> { with_media? || reblog? }
+ validates :text, presence: true, unless: -> { with_media? || reblog? || ad? }
validates_with StatusLengthValidator
validates_with DisallowedHashtagsValidator
+ validates_with StatusGroupValidator, unless: -> { performed_by_admin }
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
- validates :quote_visibility, inclusion: { in: %w(public unlisted) }, if: :quote?
+ validates :quote_visibility, inclusion: { in: %w(public unlisted group) }, if: :quote?
- accepts_nested_attributes_for :poll
-
default_scope { recent.kept }
scope :recent, -> { reorder(id: :desc) }
@@ -104,54 +136,79 @@
scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).where('accounts.domain IS NULL OR accounts.domain NOT IN (?)', account.excluded_from_timeline_domains) }
scope :tagged_with_all, ->(tag_ids) {
Array(tag_ids).reduce(self) do |result, id|
- result.joins("INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}")
+ tag_id = id.to_i
+ result.joins("INNER JOIN statuses_tags t#{tag_id} ON t#{tag_id}.status_id = statuses.id AND t#{tag_id}.tag_id = #{tag_id}")
end
}
scope :tagged_with_none, ->(tag_ids) {
Array(tag_ids).reduce(self) do |result, id|
- result.joins("LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}")
- .where("t#{id}.tag_id IS NULL")
+ tag_id = id.to_i
+ result.joins("LEFT OUTER JOIN statuses_tags t#{tag_id} ON t#{tag_id}.status_id = statuses.id AND t#{tag_id}.tag_id = #{tag_id}")
+ .where("t#{tag_id}.tag_id IS NULL")
end
}
+ scope :trending_statuses, -> { joins(:trending_status).reorder('sort_order ASC') }
+ scope :excluding_unauthorized_tv_statuses, lambda { |account_id|
+ where.not(TvProgramStatus.where('tv.program_statuses.status_id = statuses.id')
+ .where.not(Configuration::AccountEnabledFeature.where(feature_flag_id: Configuration::FeatureFlag.where(name: 'tv'), account_id: account_id).arel.exists)
+ .arel.exists)
+ }
cache_associated :application,
:media_attachments,
:conversation,
- :status_stat,
+ :status_favourite,
+ :status_reply,
+ :status_reblog,
:tags,
:preview_cards,
- :preloadable_poll,
- account: [:account_stat, :user],
- active_mentions: { account: :account_stat },
+ :ad,
+ :links,
+ tv_program: [:tv_channel],
+ account: [:account_follower, :account_following, :account_status, :user],
+ active_mentions: { account: [:account_follower, :account_following, :account_status] },
reblog: [
:application,
:tags,
:preview_cards,
:media_attachments,
:conversation,
- :status_stat,
- :preloadable_poll,
- account: [:account_stat, :user],
- active_mentions: { account: :account_stat },
+ :status_favourite,
+ :status_reply,
+ :status_reblog,
+ :ad,
+ :links,
+ account: [:account_follower, :account_following, :account_status, :user],
+ active_mentions: { account: [:account_follower, :account_following, :account_status] },
],
quote: [
- :application,
- :tags,
- :preview_cards,
- :media_attachments,
- :conversation,
- :status_stat,
- account: [:account_stat, :user],
- active_mentions: { account: :account_stat },
- ],
+ :application,
+ :tags,
+ :preview_cards,
+ :media_attachments,
+ :conversation,
+ :status_favourite,
+ :status_reply,
+ :status_reblog,
+ :links,
+ :ad,
+ account: [:account_follower, :account_following, :account_status, :user],
+ active_mentions: { account: [:account_follower, :account_following, :account_status] },
+ ],
thread: [
- :application,
- :tags,
- :preview_cards,
- :media_attachments,
- account: [:account_stat, :user],
- active_mentions: { account: :account_stat },
- ]
+ :application,
+ :tags,
+ :preview_cards,
+ :media_attachments,
+ :links,
+ :ad,
+ account: [:account_follower, :account_following, :account_status, :user],
+ active_mentions: { account: [:account_follower, :account_following, :account_status] },
+ ],
+ group: [
+ :group_stat,
+ :tags,
+ ]
delegate :domain, to: :account, prefix: true
@@ -175,14 +232,22 @@
!reblog_of_id.nil?
end
+ def ad?
+ !!(interactive_ad || ad)
+ end
+
def trending?
- trending.present?
+ trending_status.present?
end
def quote?
!quote_id.nil? && quote
end
+ def group?
+ !group_id.nil? && group
+ end
+
def quote_visibility
quote&.visibility
end
@@ -200,7 +265,13 @@
end
def object_type
- reply? ? :comment : :note
+ if group?
+ :group_note
+ elsif reply?
+ :comment
+ else
+ :note
+ end
end
def proper
@@ -224,6 +295,7 @@
end
def distributable?
+ # TODO: how do we consider group posts? they may need LDSigning for efficiency
public_visibility? || unlisted_visibility?
end
@@ -245,21 +317,20 @@
return @emojis if defined?(@emojis)
fields = [spoiler_text, text]
- fields += preloadable_poll.options unless preloadable_poll.nil?
@emojis = CustomEmoji.from_text(fields.join(' '), account.domain) + (quote? ? CustomEmoji.from_text([quote.spoiler_text, quote.text].join(' '), quote.account.domain) : [])
end
def replies_count
- status_stat&.replies_count || 0
+ status_reply&.replies_count || 0
end
def reblogs_count
- status_stat&.reblogs_count || 0
+ status_reblog&.reblogs_count || 0
end
def favourites_count
- status_stat&.favourites_count || 0
+ status_favourite&.favourites_count || 0
end
def skip_indexing?
@@ -271,20 +342,39 @@
PROHIBITED_TERMS_ON_INDEX.any? { |term| text_downcase.include? term }
end
- def increment_count!(key)
- update_status_stat!(key => public_send(key) + 1)
+ def text_hash
+ return if text.blank?
+
+ Digest::SHA2.hexdigest(text.strip)
end
- def decrement_count!(key)
- update_status_stat!(key => [public_send(key) - 1, 0].max)
+ def privatize(mod_id, _notify_user)
+ logger.debug "Status: #{id} PRIVATIZED"
+ reblogs.update_all(deleted_at: Time.current, deleted_by_id: mod_id)
+ update!(visibility: :self)
+ purge_cache
+ account.save
+ save
end
- after_create_commit :increment_counter_caches
- after_destroy_commit :decrement_counter_caches
+ def publicize
+ if visibility == 'self'
+ reblogs.update_all(deleted_at: nil, deleted_by_id: nil)
+ update!(visibility: group? ? :group : :public)
+ purge_cache
+ account.save
+ save
+ end
+ end
+ after_create_commit :increment_counter_caches, if: :group?
+ after_destroy_commit :decrement_counter_caches, if: :group?
+
after_create_commit :store_uri, if: :local?
after_create_commit :update_statistics, if: :local?
+ after_create_commit :mark_tv_status
+
around_create Mastodon::Snowflake::Callbacks
before_validation :prepare_contents, if: :local?
@@ -293,11 +383,9 @@
before_validation :set_conversation
before_validation :set_local
- after_create :set_poll_id
-
class << self
def selectable_visibilities
- visibilities.keys - %w(direct limited)
+ %w(public unlisted private)
end
def favourites_map(status_ids, account_id)
@@ -316,27 +404,76 @@
ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).each_with_object({}) { |m, h| h[m.conversation_id] = true }
end
- def pins_map(status_ids, account_id)
- StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true }
+ def pins_map(status_ids, account_id, group_id = nil)
+ StatusPin
+ .select('status_id')
+ .where(status_id: status_ids)
+ .where(account_id: account_id)
+ .where(pin_location: group_id ? :group : :profile)
+ .each_with_object({}) { |p, h| h[p.status_id] = true }
end
+ def groups_map(statuses)
+ statuses_slugs = statuses.map { |s| [s.id, extract_group_slugs(s.text)] }.to_h
+ slugs = statuses_slugs.values.uniq
+ return {} unless slugs.any?
+
+ groups = Group.where(slug: slugs).includes(:tags, :group_stat).references(:tags, :group_stat).index_by(&:slug)
+ statuses_slugs.map { |status_id, slug| [status_id, groups[slug]] }.to_h.compact
+ end
+
+ def polls_map(statuses, current_account_id)
+ statuses_with_polls = statuses.map { |s| s.id if s.has_poll }.compact.uniq
+ return {} unless statuses_with_polls.any?
+
+ rendered_polls = StatusPolls.polls(account_id: current_account_id, status_ids: statuses_with_polls)
+ return {} unless rendered_polls
+
+ rendered_polls.map { |poll| [poll['status_id'], poll['poll_json']] }.to_h.compact
+ end
+
def reload_stale_associations!(cached_items)
account_ids = []
+ status_ids = []
+ group_ids = []
cached_items.each do |item|
account_ids << item.account_id
account_ids << item.reblog.account_id if item.reblog? && item.reblog&.account_id
+ status_ids << item.id
+ group_ids << item.group_id if item.group_id
end
account_ids.uniq!
return if account_ids.empty?
- accounts = Account.where(id: account_ids).includes(:account_stat, :user).index_by(&:id)
+ accounts = Account.where(id: account_ids).includes(:account_follower, :account_following, :account_status, :user).references(:account_follower, :account_following, :account_status, :user).index_by(&:id)
+ statuses = Status.with_discarded.select([:favourites_count, :replies_count, :reblogs_count]).where(id: status_ids).includes(:status_favourite, :status_reply, :status_reblog).references(:status_favourite, :status_reply, :status_reblog).index_by(&:id)
+ groups = Group.where(id: group_ids).index_by(&:id)
cached_items.each do |item|
item.account = accounts[item.account_id]
item.reblog.account = accounts[item.reblog.account_id] if item.reblog? && item.reblog&.account_id
+ item.group = groups[item.group.id] if item.group?
+
+ if statuses[item.id].status_favourite
+ item.status_favourite = statuses[item.id].status_favourite
+ else
+ item.build_status_favourite
+ end
+
+ if statuses[item.id].status_reply
+ item.status_reply = statuses[item.id].status_reply
+ else
+ item.build_status_reply
+ end
+
+ if statuses[item.id].status_reblog
+ item.status_reblog = statuses[item.id].status_reblog
+ else
+ item.build_status_reblog
+ end
end
end
@@ -375,20 +512,26 @@
status&.distributable? ? status : nil
end
end
- end
- def status_stat
- super || build_status_stat
+ def muted_conversations_for_account(account_id)
+ sanitized_id = connection.quote(account_id.to_i)
+ select('*').from("(select s.* from statuses s
+ inner join conversations c on c.id = s.conversation_id
+ inner join conversation_mutes cm on cm.conversation_id = c.id
+ where cm.account_id = #{sanitized_id} and in_reply_to_id is null) as statuses")
+ end
+
+ def tv_channels_statuses
+ find_by_sql("
+ WITH distinct_statuses_by_channel AS(
+ select * from (select distinct on (channel_id) channel_id, status_id from tv.program_statuses tvp inner join tv.channels tvc using(channel_id) where tvc.enabled = true order by channel_id, start_time desc) sub order by channel_id asc
+ )
+ select * from statuses s inner join distinct_statuses_by_channel dsc on s.id = dsc.status_id order by s.created_at desc")
+ end
end
private
- def update_status_stat!(attrs)
- return if marked_for_destruction? || destroyed?
-
- status_stat.update(attrs)
- end
-
def store_uri
update_column(:uri, ActivityPub::TagManager.instance.uri_for(self)) if uri.nil?
end
@@ -402,10 +545,6 @@
self.reblog = reblog.reblog if reblog? && reblog.reblog?
end
- def set_poll_id
- update_column(:poll_id, poll.id) unless poll.nil?
- end
-
def set_visibility
self.visibility = reblog.visibility if reblog? && visibility.nil?
self.visibility = (account.locked? ? :private : :public) if visibility.nil?
@@ -421,6 +560,7 @@
self.in_reply_to_account_id = carried_over_reply_to_account_id
self.conversation_id = thread.conversation_id if conversation_id.nil?
redis.del("descendants:#{conversation_id}")
+ InvalidateSecondaryCacheService.new.call('InvalidateDescendantsCacheWorker', conversation_id)
elsif conversation_id.nil?
self.conversation = Conversation.new
end
@@ -445,19 +585,11 @@
end
def increment_counter_caches
- return if direct_visibility?
-
- account&.increment_count!(:statuses_count)
- reblog&.increment_count!(:reblogs_count) if reblog?
- thread&.increment_count!(:replies_count) if in_reply_to_id.present? && distributable?
+ group&.increment_count!(:statuses_count)
end
def decrement_counter_caches
- return if direct_visibility?
-
- account&.decrement_count!(:statuses_count)
- reblog&.decrement_count!(:reblogs_count) if reblog?
- thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && distributable?
+ group&.decrement_count!(:statuses_count)
end
def unlink_from_conversations
@@ -468,6 +600,19 @@
inbox_owners.each do |inbox_owner|
AccountConversation.remove_status(inbox_owner, self)
+ end
+ end
+
+ def purge_cache
+ Rails.cache.delete(self)
+ InvalidateSecondaryCacheService.new.call('InvalidateStatusCacheWorker', self)
+ end
+
+ def mark_tv_status
+ related_status_id = reblog_of_id.presence || quote_id.presence || in_reply_to_id.presence
+
+ if tv_program_status? || (related_status_id && TvStatus.find_by(status_id: related_status_id).present?)
+ TvStatus.create!(status: self)
end
end
end
Only in truth-new/opensource/app/models: status_distribution_br2.rb
Only in truth-new/opensource/app/models: status_distribution_or1.rb
Only in truth-new/opensource/app/models: status_favourite_statistic.rb
diff -ru truth-old/opensource/app/models/status_pin.rb truth-new/opensource/app/models/status_pin.rb
--- truth-old/opensource/app/models/status_pin.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/status_pin.rb 2024-04-01 14:59:13
@@ -3,16 +3,27 @@
#
# Table name: status_pins
#
-# id :bigint(8) not null, primary key
-# account_id :bigint(8) not null
-# status_id :bigint(8) not null
-# created_at :datetime not null
-# updated_at :datetime not null
+# id :bigint(8) not null, primary key
+# account_id :bigint(8) not null
+# status_id :bigint(8) not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# pin_location :enum default("profile"), not null
#
class StatusPin < ApplicationRecord
belongs_to :account
belongs_to :status
- validates_with StatusPinValidator
+ enum pin_location: { group: 'group', profile: 'profile' }, _suffix: :location
+
+ validates_with StatusPinValidator, unless: -> { is_group_pin }
+ validates_with GroupStatusPinValidator, if: -> { is_group_pin }
+
+ scope :profile_pins, -> { where(pin_location: :profile) }
+ scope :group_pins, -> { where(pin_location: :group) }
+
+ def is_group_pin
+ group_location?
+ end
end
Only in truth-new/opensource/app/models: status_polls.rb
Only in truth-new/opensource/app/models: status_reblog_statistic.rb
Only in truth-new/opensource/app/models: status_replies.rb
Only in truth-new/opensource/app/models: status_replies_v1.rb
Only in truth-new/opensource/app/models: status_replies_v2.rb
Only in truth-new/opensource/app/models: status_reply_statistic.rb
diff -ru truth-old/opensource/app/models/status_stat.rb truth-new/opensource/app/models/status_stat.rb
--- truth-old/opensource/app/models/status_stat.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/status_stat.rb 2024-04-01 14:59:13
@@ -29,8 +29,6 @@
[attributes['favourites_count'], 0].max
end
- private
-
def reset_parent_cache
Rails.cache.delete("statuses/#{status_id}")
InvalidateSecondaryCacheService.new.call("InvalidateStatusCacheWorker", status_id)
Only in truth-new/opensource/app/models: status_suggestion.rb
Only in truth-new/opensource/app/models: statuses
Only in truth-new/opensource/app/models: suggestions_carousel.rb
diff -ru truth-old/opensource/app/models/tag.rb truth-new/opensource/app/models/tag.rb
--- truth-old/opensource/app/models/tag.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/tag.rb 2024-04-01 14:59:13
@@ -8,7 +8,7 @@
# created_at :datetime not null
# updated_at :datetime not null
# usable :boolean
-# trendable :boolean
+# trendable :boolean default(TRUE), not null
# listable :boolean
# reviewed_at :datetime
# requested_review_at :datetime
@@ -18,6 +18,9 @@
#
class Tag < ApplicationRecord
+ extend Queriable
+ include Paginable
+
has_and_belongs_to_many :statuses
has_and_belongs_to_many :accounts
@@ -30,16 +33,20 @@
validates :name, presence: true, format: { with: /\A(#{HASHTAG_NAME_RE})\z/i }
validate :validate_name_change, if: -> { !new_record? && name_changed? }
+ before_create :unlist_bannable_tags
+
scope :reviewed, -> { where.not(reviewed_at: nil) }
scope :unreviewed, -> { where(reviewed_at: nil) }
scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) }
scope :usable, -> { where(usable: [true, nil]) }
scope :listable, -> { where(listable: [true, nil]) }
- scope :trendable, -> { where(trendable: true).order(last_status_at: :desc) }
+ scope :trendable, -> { where(trendable: true).where.not(max_score: nil).order(max_score: :desc, last_status_at: :desc) }
+ scope :only_trendable, -> { where(trendable: true).order(max_score: :desc, last_status_at: :desc) }
scope :recently_used, ->(account) { joins(:statuses).where(statuses: { id: account.statuses.select(:id).limit(1000) }).group(:id).order(Arel.sql('count(*) desc')) }
scope :matches_name, ->(term) { where(arel_table[:name].lower.matches("#{sanitize_sql_like(Tag.normalize(term.downcase))}%", nil, true)) } # Search with case-sensitive to use B-tree index
+ scope :search, ->(query) { where('LOWER(tags.name) LIKE :search', search: "%#{sanitize_sql_like(query&.downcase)}%") }
- update_index 'tags#tag', :self
+ update_index 'tags', :self
def contains_prohibited_terms?
name_downcase = name.downcase
@@ -91,7 +98,7 @@
def history
days = []
- 7.times do |i|
+ 1.upto(6) do |i|
day = i.days.ago.beginning_of_day.to_i
days << {
@@ -115,15 +122,9 @@
end
end
- def search_for(term, limit = 5, offset = 0, options = {})
- stripped_term = term.strip
-
- query = Tag.listable.matches_name(stripped_term)
- query = query.merge(matching_name(stripped_term).or(where.not(reviewed_at: nil))) if options[:exclude_unreviewed]
-
- query.order(Arel.sql('length(name) ASC, name ASC'))
- .limit(limit)
- .offset(offset)
+ # options = [in_search_query text, in_limit smallint, in_offset integer]
+ def search_for(*options)
+ execute_query('select mastodon_api.search_tags ($1, $2, $3)', options).to_a.first['search_tags']
end
def find_normalized(name)
@@ -150,6 +151,17 @@
end
private
+
+ def unlist_bannable_tags
+ banned_words = BannedWord.pluck(:word)
+ regexp = Regexp.new(banned_words.join('|'), true)
+ bannable = regexp === name
+
+ if bannable
+ self.listable = false
+ self.trendable = false
+ end
+ end
def validate_name_change
errors.add(:name, I18n.t('tags.does_not_match_previous_name')) unless name_was.mb_chars.casecmp(name.mb_chars).zero?
diff -ru truth-old/opensource/app/models/tag_feed.rb truth-new/opensource/app/models/tag_feed.rb
--- truth-old/opensource/app/models/tag_feed.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/tag_feed.rb 2024-04-01 14:59:13
@@ -14,6 +14,7 @@
# @option [Boolean] :only_media
def initialize(tag, account, options = {})
@tag = tag
+ @group_id = options[:group_id]
super(account, options)
end
@@ -23,7 +24,7 @@
# @param [Integer] min_id
# @return [Array<Status>]
def get(limit, max_id = nil, since_id = nil, min_id = nil)
- scope = public_scope
+ scope = @group_id ? group_scope : public_scope
scope.merge!(tagged_with_any_scope)
scope.merge!(tagged_with_all_scope)
@@ -52,5 +53,12 @@
def tags_for(names)
Tag.matching_name(Array(names).take(LIMIT_PER_MODE)).pluck(:id) if names.present?
+ end
+
+ def group_scope
+ Status.without_reblogs
+ .where(group_id: @group_id, reply: false, quote_id: nil)
+ .joins(:account)
+ .merge(Account.without_suspended.without_silenced.excluded_by_group_account_block(@group_id))
end
end
Only in truth-old/opensource/app/models: trending.rb
Only in truth-new/opensource/app/models: trending_status.rb
Only in truth-new/opensource/app/models: trending_status_excluded_expression.rb
Only in truth-new/opensource/app/models: trending_status_excluded_status.rb
Only in truth-new/opensource/app/models: trending_status_setting.rb
diff -ru truth-old/opensource/app/models/trending_tags.rb truth-new/opensource/app/models/trending_tags.rb
--- truth-old/opensource/app/models/trending_tags.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/trending_tags.rb 2024-04-01 14:59:13
@@ -13,23 +13,11 @@
class << self
include Redisable
- def record_use!(tag, account, status: nil, at_time: Time.now.utc)
- return unless tag.usable? && !account.silenced?
+ def record_use!(tag, account, status: nil, at_time: Time.now.utc); end
- # Even if a tag is not allowed to trend, we still need to
- # record the stats since they can be displayed in other places
- increment_historical_use!(tag.id, at_time)
- increment_unique_use!(tag.id, account.id, at_time)
- increment_use!(tag.id, at_time)
-
- # Only update when the tag was last used once every 12 hours
- # and only if a status is given (lets use ignore reblogs)
- tag.update(last_status_at: at_time) if status.present? && (tag.last_status_at.nil? || (tag.last_status_at < at_time && tag.last_status_at < 12.hours.ago))
- end
-
def update!(at_time = Time.now.utc)
tag_ids = redis.smembers("#{KEY}:used:#{at_time.beginning_of_day.to_i}") + redis.zrange(KEY, 0, -1)
- tags = Tag.trendable.where(id: tag_ids.uniq)
+ tags = Tag.where(trendable: true).where(id: tag_ids.uniq)
# First pass to calculate scores and update the set
Only in truth-new/opensource/app/models: trending_tags_result.rb
Only in truth-new/opensource/app/models: tv_account.rb
Only in truth-new/opensource/app/models: tv_carousel.rb
Only in truth-new/opensource/app/models: tv_channel.rb
Only in truth-new/opensource/app/models: tv_channel_account.rb
Only in truth-new/opensource/app/models: tv_device_session.rb
Only in truth-new/opensource/app/models: tv_program.rb
Only in truth-new/opensource/app/models: tv_program_status.rb
Only in truth-new/opensource/app/models: tv_program_temporary.rb
Only in truth-new/opensource/app/models: tv_reminder.rb
Only in truth-new/opensource/app/models: tv_status.rb
diff -ru truth-old/opensource/app/models/user.rb truth-new/opensource/app/models/user.rb
--- truth-old/opensource/app/models/user.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/user.rb 2024-04-05 09:07:34
@@ -3,7 +3,6 @@
#
# Table name: users
#
-# id :bigint(8) not null, primary key
# email :string default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
@@ -31,6 +30,7 @@
# otp_backup_codes :string is an Array
# filtered_languages :string default([]), not null, is an Array
# account_id :bigint(8) not null
+# id :bigint(8) not null, primary key
# disabled :boolean default(FALSE), not null
# moderator :boolean default(FALSE), not null
# invite_id :bigint(8)
@@ -46,12 +46,16 @@
# waitlist_position :integer
# unsubscribe_from_emails :boolean default(FALSE)
# ready_to_approve :integer default("not_ready_for_approval")
-# unauth_visibility :boolean
+# unauth_visibility :boolean default(TRUE), not null
+# policy_id :bigint(8)
+# sign_up_city_id :integer not null
+# sign_up_country_id :integer not null
#
class User < ApplicationRecord
include Settings::Extend
include UserRoles
+ include EmailHelper
# The home and list feeds will be stored in Redis for this amount
# of time, and status fan-out to followers will include only people
@@ -61,7 +65,15 @@
# RegenerationWorker jobs that need to be run when those people come
# to check their feed
ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days.freeze
- WAITLIST_PADDING = ENV.fetch('WAITLIST_PADDING', 50000).to_i
+ WAITLIST_PADDING = ENV.fetch('WAITLIST_PADDING', 50_000).to_i
+ BASE_EMAIL_DOMAINS_VALIDATION = ENV.fetch('BASE_EMAIL_DOMAINS_VALIDATION', false)
+ VERIFICATION_INTERVAL = 1.hour.ago.freeze
+ INTEGRITY_STATUSES = {
+ favourite: 'favourite',
+ status: 'status',
+ chat_message: 'chat_message',
+ reblog: 'reblog',
+ }.freeze
devise :two_factor_authenticatable,
otp_secret_encryption_key: Rails.configuration.x.otp_secret
@@ -79,6 +91,9 @@
belongs_to :account, inverse_of: :user
belongs_to :invite, counter_cache: :uses, optional: true
belongs_to :created_by_application, class_name: 'Doorkeeper::Application', optional: true
+ belongs_to :policy, optional: true
+ belongs_to :city, class_name: 'City', foreign_key: 'sign_up_city_id', optional: true
+ belongs_to :country, class_name: 'Country', foreign_key: 'sign_up_country_id', optional: true
accepts_nested_attributes_for :account
has_many :applications, class_name: 'Doorkeeper::Application', as: :owner
@@ -86,15 +101,16 @@
has_many :invites, inverse_of: :user
has_many :markers, inverse_of: :user, dependent: :destroy
has_many :webauthn_credentials, dependent: :destroy
+ has_many :one_time_challenges, dependent: :destroy
+ has_many :password_histories, class_name: 'PasswordHistory'
has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy
- has_one :trending
+ has_one :user_current_information
accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text }
validates :invite_request, presence: true, on: :create, if: :invite_text_required?
validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?
validates_with BlacklistedEmailValidator, on: :create
- validates_with EmailMxValidator, if: :validate_email_dns?
validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create
# Those are honeypot/antispam fields
@@ -102,8 +118,11 @@
validates_with RegistrationFormTimeValidator, on: :create
validates :website, absence: true, on: :create
+ validates :password, unique_password: true
validates :confirm_password, absence: true, on: :create
+ validates_with BaseEmailValidator, on: :create
+
scope :recent, -> { order(id: :desc) }
scope :pending, -> { where(approved: false) }
scope :approved, -> { where(approved: true) }
@@ -122,6 +141,8 @@
before_create :skip_confirmation_if_invited
after_commit :send_pending_devise_notifications
after_update_commit :send_approved_notification
+ after_create :create_base_email
+ after_save :store_password_history
# This avoids a deprecation warning from Rails 5.1
# It seems possible that a future release of devise-two-factor will
@@ -130,6 +151,11 @@
has_many :session_activations, dependent: :destroy
+ has_one :user_base_email
+
+ has_one :user_sms_reverification_required
+ scope :with_reverification, -> { eager_load(:user_sms_reverification_required) }
+
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
@@ -141,7 +167,7 @@
attr_writer :external, :bypass_invite_request_check
enum ready_to_approve: { not_ready_for_approval: 0, ready_by_csv_import: 1, ready_by_sms_verification: 2, sent_one_push_notification: 3, sent_two_push_notifications: 4, sent_three_push_notifications: 5 }
- self.ignored_columns = ["reviewed_for_approval"]
+ self.ignored_columns = ['reviewed_for_approval']
def confirmed?
confirmed_at.present?
@@ -185,7 +211,7 @@
end
def confirm!
- new_user = !confirmed?
+ new_user = !confirmed?
skip_confirmation!
save!
@@ -209,12 +235,6 @@
if new_sign_in
self.sign_in_count ||= 0
self.sign_in_count += 1
- else
- query = if old_current_sign_in.nil?
- query.where('current_sign_in_at' => nil)
- else
- query.where('current_sign_in_at < :time', time: UserTrackingConcern::UPDATE_SIGN_IN_HOURS.hours.ago)
- end
end
unless new_record?
@@ -224,6 +244,17 @@
current_sign_in_ip: current_sign_in_ip,
sign_in_count: sign_in_count)
end
+
+ UserCurrentInformation.upsert(
+ user_id: id,
+ current_sign_in_at: new_current_sign_in,
+ current_sign_in_ip: new_current_ip,
+ current_city_id: geo(request).city,
+ current_country_id: geo(request).country
+ )
+
+ EventProvider::EventProvider.new('session.updated', SessionUpdatedEvent, { user_id: id, account_id: account_id, ip_address: new_current_ip, timestamp: new_current_sign_in }).call
+
prepare_returning_user!
end
@@ -232,13 +263,13 @@
end
def self.get_user_from_token(user_token)
- id, _updated_at_s = EncryptAttrService.decrypt(user_token).split("+=")
+ id, _updated_at_s = EncryptAttrService.decrypt(user_token).split('+=')
find_by(id: id)
end
def validate_user_token(user_token)
- _id, updated_at_s = EncryptAttrService.decrypt(user_token).split("+=")
+ _id, updated_at_s = EncryptAttrService.decrypt(user_token).split('+=')
updated_at.to_s == updated_at_s
end
@@ -251,6 +282,23 @@
sms.present?
end
+ # remove once all devices have completed the force update
+ def integrity_score
+ return 0 unless ActiveModel::Type::Boolean.new.cast(ENV.fetch('PLAY_INTEGRITY_ENABLED', true)) # Enable/Disable app integrity for all users
+
+ last_status_at = AccountStatusStatistic.find_by(account_id: account.id)&.last_status_at
+ first_status_today = last_status_at ? last_status_at < Time.zone.now.midnight : true
+ first_status_today ? 1 : 0
+ end
+
+ def integrity_status(token, android_client)
+ return [] unless android_client
+ return [] unless user_sms_reverification_required
+
+ integrity_credential = token.integrity_credentials.order(last_verified_at: :desc).first
+ integrity_credential&.last_verified_at&.send(:>, VERIFICATION_INTERVAL) ? [] : INTEGRITY_STATUSES.values
+ end
+
def active_for_authentication?
!account.memorial?
end
@@ -288,7 +336,7 @@
end
def two_factor_enabled?
- otp_required_for_login? || webauthn_credentials.any?
+ otp_required_for_login?
end
def disable_two_factor!
@@ -296,8 +344,6 @@
self.otp_secret = nil
otp_backup_codes&.clear
- webauthn_credentials.destroy_all if webauthn_enabled?
-
save!
end
@@ -305,7 +351,7 @@
return 0 if approved?
most_recent_user = User.pending.order(waitlist_position: :desc).first
- position = most_recent_user&.waitlist_position || 11342 # this is a magic number means nothing could be anything
+ position = most_recent_user&.waitlist_position || 11_342 # this is a magic number means nothing could be anything
self.waitlist_position = position + 1
save!
@@ -357,7 +403,7 @@
# rubocop:disable Naming/MethodParameterName
def token_for_app(a)
return nil if a.nil? || a.owner != self
- Doorkeeper::AccessToken.find_or_create_by(application_id: a.id, resource_owner_id: id) do |t|
+ OauthAccessToken.find_or_create_by(application_id: a.id, resource_owner_id: id) do |t|
t.scopes = a.scopes
t.expires_in = Doorkeeper.configuration.access_token_expires_in
t.use_refresh_token = Doorkeeper.configuration.refresh_token_enabled?
@@ -456,6 +502,10 @@
nil
end
+ def sms_country
+ Phonelib.parse(sms).country
+ end
+
protected
def send_devise_notification(notification, *args, **kwargs)
@@ -540,7 +590,7 @@
def prepare_returning_user!
ActivityTracker.record('activity:logins', id)
- regenerate_feed! if needs_feed_update?
+ clear_feeds! if needs_feed_update?
end
def notify_staff_about_pending_account!
@@ -554,6 +604,13 @@
RegenerationWorker.perform_async(account_id) if Redis.current.set("account:#{account_id}:regeneration", true, nx: true, ex: 1.day.seconds)
end
+ def clear_feeds!
+ home_feed = HomeFeed.new(account)
+ home_feed.clear!
+ groups_feed = GroupsFeed.new(account)
+ groups_feed.clear!
+ end
+
def needs_feed_update?
last_sign_in_at < ACTIVE_DURATION.ago
end
@@ -573,16 +630,40 @@
end
def hourly_limit_reached?
- key = "approved_users_per_hour:#{DateTime.current.strftime("%Y-%m-%d:%H:00")}"
+ key = "approved_users_per_hour:#{DateTime.current.strftime('%Y-%m-%d:%H:00')}"
return unless (limit_per_hour = ENV['USERS_PER_HOUR'].to_i) > 0
current_limit = Redis.current.scard(key)
current_limit.present? && current_limit.to_i >= limit_per_hour
end
def track_approved_user
- key = "approved_users_per_hour:#{DateTime.current.strftime("%Y-%m-%d:%H:00")}"
+ key = "approved_users_per_hour:#{DateTime.current.strftime('%Y-%m-%d:%H:00')}"
Redis.current.sadd(key, id)
Redis.current.expire(key, 65.minutes.seconds)
- Prometheus::ApplicationExporter::increment(:approves)
+ Prometheus::ApplicationExporter.increment(:approves)
+ end
+
+ def geo(request)
+ @geo_object ||= GeoService.new(
+ city_name: request.headers['Geoip-City-Name'],
+ country_code: request.headers['Geoip-Country-Code'],
+ country_name: request.headers['Geoip-Country-Name'],
+ region_name: request.headers['Geoip-Region-Name'],
+ region_code: request.headers['Geoip-Region-Code']
+ )
+ end
+
+ def create_base_email
+ return unless BASE_EMAIL_DOMAINS_VALIDATION
+
+ username, domain = email_to_canonical_email_by_username_and_domain(email).values_at(:username, :domain)
+
+ return unless BASE_EMAIL_DOMAINS_VALIDATION.split(',').map(&:strip).include? domain
+
+ UserBaseEmail.create(user_id: id, email: "#{username}@#{domain}")
+ end
+
+ def store_password_history
+ PasswordHistory.create!(user: self, encrypted_password: encrypted_password) if saved_change_to_encrypted_password?
end
end
Only in truth-new/opensource/app/models: user_base_email.rb
Only in truth-new/opensource/app/models: user_current_information.rb
Only in truth-new/opensource/app/models: user_sms_reverification_required.rb
Only in truth-new/opensource/app/models: users_one_time_challenge.rb
diff -ru truth-old/opensource/app/models/web/push_subscription.rb truth-new/opensource/app/models/web/push_subscription.rb
--- truth-old/opensource/app/models/web/push_subscription.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/web/push_subscription.rb 2024-04-01 14:59:13
@@ -19,7 +19,7 @@
class Web::PushSubscription < ApplicationRecord
belongs_to :user, optional: true
- belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', optional: true
+ belongs_to :access_token, class_name: 'OauthAccessToken', optional: true
has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription
@@ -74,7 +74,7 @@
class << self
def unsubscribe_for(application_id, resource_owner)
- access_token_ids = Doorkeeper::AccessToken.where(application_id: application_id, resource_owner_id: resource_owner.id, revoked_at: nil).pluck(:id)
+ access_token_ids = OauthAccessToken.where(application_id: application_id, resource_owner_id: resource_owner.id, revoked_at: nil).pluck(:id)
where(access_token_id: access_token_ids).delete_all
end
end
@@ -82,7 +82,7 @@
private
def find_or_create_access_token
- Doorkeeper::AccessToken.find_or_create_for(
+ OauthAccessToken.find_or_create_for(
application: Doorkeeper::Application.find_by(superapp: true),
resource_owner: user_id || session_activation.user_id,
scopes: Doorkeeper::OAuth::Scopes.from_string('read write follow push'),
diff -ru truth-old/opensource/app/models/webauthn_credential.rb truth-new/opensource/app/models/webauthn_credential.rb
--- truth-old/opensource/app/models/webauthn_credential.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/models/webauthn_credential.rb 2024-04-01 14:59:13
@@ -3,20 +3,65 @@
#
# Table name: webauthn_credentials
#
-# id :bigint(8) not null, primary key
-# external_id :string not null
-# public_key :string not null
-# nickname :string not null
-# sign_count :bigint(8) default(0), not null
-# user_id :bigint(8)
-# created_at :datetime not null
-# updated_at :datetime not null
+# id :bigint(8) not null, primary key
+# external_id :string not null
+# public_key :string not null
+# nickname :string not null
+# sign_count :bigint(8) default(0), not null
+# user_id :bigint(8)
+# created_at :datetime not null
+# updated_at :datetime not null
+# receipt :text
+# fraud_metric :integer
+# receipt_updated_at :datetime
+# baseline_fraud_metric :integer default(0), not null
+# sandbox :boolean default(FALSE), not null
#
class WebauthnCredential < ApplicationRecord
+ extend AppAttestable
+
validates :external_id, :public_key, :nickname, :sign_count, presence: true
validates :external_id, uniqueness: true
validates :nickname, uniqueness: { scope: :user_id }
validates :sign_count,
numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 2**63 - 1 }
+
+ has_one :one_time_challenge
+ has_one :registration_webauthn_credential
+ has_one :registration, through: :registration_webauthn_credential
+ belongs_to :user, optional: true
+ has_many :token_credentials, class_name: 'OauthAccessTokens::WebauthnCredential'
+
+ class << self
+ def decode_receipt(encoded_receipt:, all_fields: false)
+ receipt = Base64.strict_decode64(encoded_receipt)
+ certificates, payload = verify_and_decode receipt
+ fields_hash = extract_field_values(payload)
+
+ {
+ **user_sensitive_fields(all_fields, certificates, fields_hash),
+ receipt_type: fields_hash[6],
+ creation_time: fields_hash[12],
+ risk_metric: fields_hash[17],
+ not_before: fields_hash[19],
+ expiration_time: fields_hash[21],
+ }
+ end
+
+ private
+
+ def user_sensitive_fields(all_fields, certificates, fields_hash)
+ return {} unless all_fields
+
+ {
+ certificate_chain: certificates,
+ app_id: fields_hash[2],
+ attested_public_key: fields_hash[3],
+ client_hash: fields_hash[4],
+ token: fields_hash[5],
+ }
+ end
+ end
end
+
Only in truth-new/opensource/app/policies: feed_policy.rb
Only in truth-new/opensource/app/policies: group_membership_policy.rb
Only in truth-new/opensource/app/policies: group_membership_request_policy.rb
Only in truth-new/opensource/app/policies: group_policy.rb
diff -ru truth-old/opensource/app/policies/poll_policy.rb truth-new/opensource/app/policies/poll_policy.rb
--- truth-old/opensource/app/policies/poll_policy.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/policies/poll_policy.rb 2024-04-01 14:59:13
@@ -2,6 +2,6 @@
class PollPolicy < ApplicationPolicy
def vote?
- StatusPolicy.new(current_account, record.status).show? && !current_account.blocking?(record.account) && !record.account.blocking?(current_account)
+ StatusPolicy.new(current_account, record.status).show? && !current_account.blocking?(record.status.account) && !record.status.account.blocking?(current_account)
end
end
diff -ru truth-old/opensource/app/policies/status_policy.rb truth-new/opensource/app/policies/status_policy.rb
--- truth-old/opensource/app/policies/status_policy.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/policies/status_policy.rb 2024-04-12 09:09:08
@@ -14,10 +14,14 @@
def show?
return false if author.suspended?
- if requires_mention?
+ if group?
+ owned? || public_group? || private_group_member?
+ elsif requires_mention?
owned? || mention_exists?
elsif private?
owned? || following_author? || mention_exists?
+ elsif tv?
+ current_account.tv_enabled?
else
current_account.nil? || (!author_blocking? && !author_blocking_domain?)
end
@@ -51,10 +55,18 @@
author.id == current_account&.id
end
+ def group?
+ record.group_visibility?
+ end
+
def private?
record.private_visibility?
end
+ def tv?
+ !!record.tv_program
+ end
+
def mention_exists?
return false if current_account.nil?
@@ -87,6 +99,22 @@
return false if current_account.nil?
@preloaded_relations[:following] ? @preloaded_relations[:following][author.id] : current_account.following?(author)
+ end
+
+ def public_group?
+ record.group.everyone?
+ end
+
+ def private_group_member?
+ return false unless record.group.members_only?
+
+ group_member?
+ end
+
+ def group_member?
+ return false if current_account.nil? || record.group_id.nil?
+
+ record.group.members.where(id: current_account&.id).exists?
end
def author
diff -ru truth-old/opensource/app/policies/user_policy.rb truth-new/opensource/app/policies/user_policy.rb
--- truth-old/opensource/app/policies/user_policy.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/policies/user_policy.rb 2024-04-01 14:59:13
@@ -49,6 +49,20 @@
admin? && !record.admin? && demoteable?
end
+ def enable_sms_reverification?
+ admin?
+ end
+
+ def disable_sms_reverification?
+ admin?
+ end
+
+
+ def enable_feature?
+ admin?
+ end
+
+
private
def promoteable?
diff -ru truth-old/opensource/app/presenters/account_relationships_presenter.rb truth-new/opensource/app/presenters/account_relationships_presenter.rb
--- truth-old/opensource/app/presenters/account_relationships_presenter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/presenters/account_relationships_presenter.rb 2024-04-01 14:59:13
@@ -49,36 +49,38 @@
account_note: {},
}
- @uncached_account_ids = []
+ @uncached_account_ids = @account_ids.uniq
- @account_ids.each do |account_id|
- maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}")
-
- if maps_for_account.is_a?(Hash)
- @cached.deep_merge!(maps_for_account)
- else
- @uncached_account_ids << account_id
- end
+ cache_ids = @account_ids.map { |account_id| relationship_cache_key(account_id) }
+ Rails.cache.read_multi(*cache_ids).each do |key, maps_for_account|
+ @cached.deep_merge!(maps_for_account)
+ @uncached_account_ids.delete(key.last)
end
@cached
end
def cache_uncached!
- @uncached_account_ids.each do |account_id|
+ to_cache = @uncached_account_ids.to_h do |account_id|
maps_for_account = {
- following: { account_id => following[account_id] },
- followed_by: { account_id => followed_by[account_id] },
- blocking: { account_id => blocking[account_id] },
- blocked_by: { account_id => blocked_by[account_id] },
- muting: { account_id => muting[account_id] },
- requested: { account_id => requested[account_id] },
+ following: { account_id => following[account_id] },
+ followed_by: { account_id => followed_by[account_id] },
+ blocking: { account_id => blocking[account_id] },
+ blocked_by: { account_id => blocked_by[account_id] },
+ muting: { account_id => muting[account_id] },
+ requested: { account_id => requested[account_id] },
domain_blocking: { account_id => domain_blocking[account_id] },
- endorsed: { account_id => endorsed[account_id] },
- account_note: { account_id => account_note[account_id] },
+ endorsed: { account_id => endorsed[account_id] },
+ account_note: { account_id => account_note[account_id] },
}
- Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day)
+ [relationship_cache_key(account_id), maps_for_account]
end
+
+ Rails.cache.write_multi(to_cache, expires_in: 1.day)
+ end
+
+ def relationship_cache_key(account_id)
+ ['relationship', @current_account_id, account_id]
end
end
Only in truth-new/opensource/app/presenters: feed_relationships_presenter.rb
Only in truth-new/opensource/app/presenters: group_relationships_presenter.rb
diff -ru truth-old/opensource/app/presenters/instance_presenter.rb truth-new/opensource/app/presenters/instance_presenter.rb
--- truth-old/opensource/app/presenters/instance_presenter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/presenters/instance_presenter.rb 2024-04-01 14:59:13
@@ -29,7 +29,7 @@
end
def status_count
- Rails.cache.fetch('local_status_count') { Account.local.joins(:account_stat).sum('account_stats.statuses_count') }.to_i
+ Rails.cache.fetch('local_status_count') { Account.local.joins(:account_status).sum('statuses_count') }.to_i
end
def domain_count
diff -ru truth-old/opensource/app/presenters/status_relationships_presenter.rb truth-new/opensource/app/presenters/status_relationships_presenter.rb
--- truth-old/opensource/app/presenters/status_relationships_presenter.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/presenters/status_relationships_presenter.rb 2024-04-01 14:59:13
@@ -2,26 +2,32 @@
class StatusRelationshipsPresenter
attr_reader :reblogs_map, :favourites_map, :mutes_map, :pins_map,
- :bookmarks_map
+ :bookmarks_map, :groups_map, :polls_map
- def initialize(statuses, current_account_id = nil, **options)
+ def initialize(statuses, current_account_id = nil, group_id = nil, **options)
if current_account_id.nil?
@reblogs_map = {}
@favourites_map = {}
@bookmarks_map = {}
+ @groups_map = {}
@mutes_map = {}
@pins_map = {}
+ @polls_map = {}
else
statuses = statuses.compact
status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.uniq.compact
conversation_ids = statuses.filter_map(&:conversation_id).uniq
pinnable_status_ids = statuses.map(&:proper).filter_map { |s| s.id if s.account_id == current_account_id && %w(public unlisted).include?(s.visibility) }
+ pinnable_group_status_ids = statuses.map(&:proper).filter_map { |s| s.id if s.group_visibility? }
@reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(options[:reblogs_map] || {})
@favourites_map = Status.favourites_map(status_ids, current_account_id).merge(options[:favourites_map] || {})
- @bookmarks_map = Status.bookmarks_map(status_ids, current_account_id).merge(options[:bookmarks_map] || {})
+ @bookmarks_map = {}
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(options[:mutes_map] || {})
- @pins_map = Status.pins_map(pinnable_status_ids, current_account_id).merge(options[:pins_map] || {})
+ @group_pins_map = Status.pins_map(pinnable_group_status_ids, current_account_id, group_id).merge(options[:pins_map] || {})
+ @pins_map = group_id ? @group_pins_map : Status.pins_map(pinnable_status_ids, current_account_id, group_id).merge(options[:pins_map] || {})
+ @groups_map = Status.groups_map(statuses)
+ @polls_map = Status.polls_map(statuses, current_account_id)
end
end
end
Only in truth-new/opensource/app/presenters: v2
diff -ru truth-old/opensource/app/serializers/activitypub/actor_serializer.rb truth-new/opensource/app/serializers/activitypub/actor_serializer.rb
--- truth-old/opensource/app/serializers/activitypub/actor_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/activitypub/actor_serializer.rb 2024-04-01 14:59:13
@@ -10,7 +10,7 @@
:discoverable, :olm, :suspended
attributes :id, :type, :following, :followers,
- :inbox, :outbox, :featured, :featured_tags,
+ :featured, :featured_tags,
:preferred_username, :name, :summary,
:url, :manually_approves_followers,
:discoverable, :published
@@ -27,12 +27,6 @@
class EndpointsSerializer < ActivityPub::Serializer
include RoutingHelper
-
- attributes :shared_inbox
-
- def shared_inbox
- inbox_url
- end
end
has_one :endpoints, serializer: EndpointsSerializer
@@ -43,7 +37,7 @@
delegate :suspended?, :instance_actor?, to: :object
def id
- object.instance_actor? ? instance_actor_url : account_url(object)
+ account_url(object)
end
def type
@@ -66,16 +60,8 @@
account_followers_url(object)
end
- def inbox
- object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object)
- end
-
def devices
account_collection_url(object, :devices)
- end
-
- def outbox
- object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object)
end
def featured
diff -ru truth-old/opensource/app/serializers/manifest_serializer.rb truth-new/opensource/app/serializers/manifest_serializer.rb
--- truth-old/opensource/app/serializers/manifest_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/manifest_serializer.rb 2023-05-05 13:42:02
@@ -68,6 +68,48 @@
sizes: '512x512',
type: 'image/png',
},
+ {
+ src: '/icons/icon-maskable-48x48.png',
+ sizes: '48x48',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/icons/icon-maskable-72x72.png',
+ sizes: '72x72',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/icons/icon-maskable-96x96.png',
+ sizes: '96x96',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/icons/icon-maskable-128x128.png',
+ sizes: '128x128',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/icons/icon-maskable-192x192.png',
+ sizes: '192x192',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/icons/icon-maskable-384x384.png',
+ sizes: '384x384',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/icons/icon-maskable-512x512.png',
+ sizes: '512x512',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
]
end
diff -ru truth-old/opensource/app/serializers/mobile/notification_serializer.rb truth-new/opensource/app/serializers/mobile/notification_serializer.rb
--- truth-old/opensource/app/serializers/mobile/notification_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/mobile/notification_serializer.rb 2024-04-12 09:09:08
@@ -1,11 +1,14 @@
# frozen_string_literal: true
-class Mobile::NotificationSerializer < ActiveModel::Serializer
+class Mobile::NotificationSerializer < NotificationSerializer
include RoutingHelper
include ActionView::Helpers::TextHelper
include ActionView::Helpers::SanitizeHelper
attributes :token, :category, :platform, :message, :extend
+ attribute :title, if: :chat?
+ attribute :mutable_content
+ attribute :thread_id, if: :chat?
def token
[current_push_subscription.device_token]
@@ -23,29 +26,52 @@
object_type
end
+ def chat?
+ object.type == :chat
+ end
+
delegate :platform, to: :current_push_subscription
def message
- params = {name: object.from_account.display_name.presence || object.from_account.username}
- if object.count.to_i > 1
- template = "#{object_type}_group"
- params[:count_others] = object.count - 1
- params[:actor] = "other"
- params[:actor] += "s" if object.count.to_i > 2
- else
- template = object_type
- end
- I18n.t("notification_mailer.#{template}.subject", params)
+ chat? ? chat_message : I18n.t("notification_mailer.#{template}.#{notification_mailer_subject}", template_params)
end
+ def title
+ "@#{object.from_account.username}"
+ end
+
+ def title_with_display_name
+ object.from_account.display_name.presence || "@#{object.from_account.username}"
+ end
+
+ def mutable_content
+ true
+ end
+
+ def thread_id
+ chat_message_object['id']
+ end
+
def extend
url = notification_url(object.type)
- if (url.nil? || url.empty?)
+ if url.nil? || url.empty?
Rails.logger.info("Empty mobile push notification status detected. Object ID: #{object.id}. Object Type: #{object.type}")
end
- [{"key" => "truthLink", "val" => url}]
+ payload = []
+ payload.push({ 'key' => 'truthLink', 'val' => url })
+ payload.push({ 'key' => 'title', 'val' => chat? ? title_with_display_name : 'Truth Social' }) unless android?
+ payload.push({ 'key' => 'accountId', 'val' => object.account_id.to_s })
+ payload.push({ 'key' => 'chat', 'val' => extended_chat_fields }) if extended_chat_fields.present?
+ payload.push({ 'key' => 'category', 'val' => object_type })
+
+ if android?
+ payload.push({ 'key' => 'fromAccountId', 'val' => object.from_account_id.to_s })
+ payload.push({ 'key' => 'title', 'val' => android_title })
+ end
+
+ payload
end
def body
@@ -53,18 +79,101 @@
truncate(HTMLEntities.new.decode(str.to_str), length: 140) # Do not encode entities, since this value will not be used in HTML
end
+ def extended_chat_fields
+ return unless chat?
+
+ attachments = chat_message_attachments ? { 'media_attachments': chat_message_attachments } : {}
+ if android?
+ {
+ 'title': title_with_display_name,
+ 'chat_message_id': chat_message_object['id'],
+ 'chat_message_created_at': chat_message_object['created_at'],
+ 'from_account_id': chat_message_object['account_id'],
+ **attachments,
+ }
+ else
+ attachments
+ end
+ end
+
+ def chat_message_object
+ message = ChatMessage.find_message(object.account_id, object.activity.chat_id, object.activity_id)
+ ActiveSupport::JSON.decode(message) if message
+ end
+
+ def chat_message
+ chat_message_object['content'] ? strip_tags(chat_message_object['content']) : I18n.t("notification_mailer.chat.sent_message")
+ end
+
+ def chat_message_attachments
+ chat_message_object['media_attachments']
+ .pluck('id', 'type', 'preview_url')
+ .map { |p| { id: p[0], type: p[1], preview_url: p[2] } } if chat_message_object['media_attachments']
+ end
+
private
+
def notification_url(type)
- if %i[reblog reblog_group mention mention_group favourite favourite_group].include? type
+ if %i(reblog
+ reblog_group
+ mention
+ mention_group
+ favourite
+ favourite_group
+ ).include? type
object.target_status.uri
- elsif %i[follow follow_group].include? type
+ elsif %i(
+ follow
+ follow_group).include? type
ActivityPub::TagManager.instance.url_for(object.from_account)
+ elsif %i(
+ group_request
+ group_approval
+ group_promoted
+ group_demoted
+ group_delete).include? type
+ ActivityPub::TagManager.instance.url_for(object.target_group)
+ elsif %i(
+ group_favourite
+ group_favourite_group
+ group_mention
+ group_mention_group
+ group_reblog
+ group_reblog_group).include? type
+ ActivityPub::TagManager.instance.url_for(object.target_status)
+ elsif type == :chat
+ ActivityPub::TagManager.instance.url_for_chat_message(object.activity_id)
else
- ""
+ ''
end
end
def object_type
object.type.to_s.gsub '_group', ''
+ end
+
+ def android?
+ platform == 2
+ end
+
+ def notification_mailer_subject
+ android? ? 'subject_android' : 'subject'
+ end
+
+ def template_params
+ @template_params ||= mailer_params
+ end
+
+ def android_title
+ handle = "@#{object.from_account.username}"
+ if %i(group_request
+ group_approval
+ group_promoted
+ group_demoted
+ group_delete).include? object.type
+ template_params[:group] || handle
+ else
+ handle
+ end
end
end
Only in truth-new/opensource/app/serializers: notification_serializer.rb
diff -ru truth-old/opensource/app/serializers/oembed_serializer.rb truth-new/opensource/app/serializers/oembed_serializer.rb
--- truth-old/opensource/app/serializers/oembed_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/oembed_serializer.rb 2024-04-01 14:59:13
@@ -39,7 +39,7 @@
def html
attributes = {
src: embed_short_account_status_url(object.account, object),
- class: 'mastodon-embed',
+ class: "truthsocial-embed#{' truthsocial-video' if has_video}",
style: 'max-width: 100%; border: 0',
width: width,
height: height,
@@ -55,5 +55,9 @@
def height
instance_options[:height]
+ end
+
+ def has_video
+ instance_options[:has_video]
end
end
diff -ru truth-old/opensource/app/serializers/rest/account_serializer.rb truth-new/opensource/app/serializers/rest/account_serializer.rb
--- truth-old/opensource/app/serializers/rest/account_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/account_serializer.rb 2024-04-05 09:07:34
@@ -5,7 +5,8 @@
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
:note, :url, :avatar, :avatar_static, :header, :header_static, :followers_count,
- :following_count, :statuses_count, :last_status_at, :verified, :location, :website
+ :following_count, :statuses_count, :last_status_at, :verified, :location, :website, :accepting_messages, :chats_onboarded,
+ :feeds_onboarded, :tv_onboarded, :show_nonmember_group_statuses, :pleroma, :tv_account, :receive_only_follow_mentions
has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
@@ -29,10 +30,10 @@
delegate :verified?, to: :object
- delegate :location, to: :object
+ def website
+ object.suspended? ? '' : object.website
+ end
- delegate :website, to: :object
-
def acct
object.pretty_acct
end
@@ -42,7 +43,7 @@
end
def url
- ActivityPub::TagManager.instance.url_for(object)
+ object.suspended? ? '' : ActivityPub::TagManager.instance.url_for(object)
end
def avatar
@@ -54,15 +55,23 @@
end
def header
- object&.header_file_name ? full_asset_url(object.suspended? ? object.header.default_url : object.header_original_url) : ''
+ if object&.header_file_name
+ full_asset_url(object.suspended? ? object.header.default_url : object.header_original_url)
+ else
+ ''
+ end
end
def header_static
- object&.header_file_name ? full_asset_url(object.suspended? ? object.header.default_url : object.header_static_url) : ''
+ if object&.header_file_name
+ full_asset_url(object.suspended? ? object.header.default_url : object.header_static_url)
+ else
+ ''
+ end
end
def created_at
- object.created_at.midnight.as_json
+ object.created_at.as_json
end
def last_status_at
@@ -101,9 +110,27 @@
object.suspended?
end
+ def pleroma
+ {
+ accepts_chat_messages: object.accepting_messages,
+ }
+ end
+
+ def location
+ object.suspended? ? '' : object.location
+ end
+
delegate :suspended?, to: :object
def moved_and_not_nested?
object.moved? && object.moved_to_account.moved_to_account_id.nil?
+ end
+
+ def tv_account
+ instance_options[:tv_account_lookup] && instance_options[:tv_account_lookup] == true ? !!object.tv_channel_account : false
+ end
+
+ def chats_onboarded
+ true
end
end
Only in truth-new/opensource/app/serializers/rest: ad_metric_serializer.rb
diff -ru truth-old/opensource/app/serializers/rest/admin/account_serializer.rb truth-new/opensource/app/serializers/rest/admin/account_serializer.rb
--- truth-old/opensource/app/serializers/rest/admin/account_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/admin/account_serializer.rb 2024-04-01 14:59:13
@@ -1,10 +1,10 @@
# frozen_string_literal: true
class REST::Admin::AccountSerializer < ActiveModel::Serializer
- attributes :id, :username, :domain, :created_at,
+ attributes :id, :username, :domain, :created_at, :deleted,
:email, :ip, :role, :confirmed, :suspended,
:silenced, :disabled, :approved, :locale,
- :invite_request, :verified, :location, :website
+ :invite_request, :verified, :location, :website, :sms, :sms_reverification_required, :updated_at, :advertiser
attribute :created_by_application_id, if: :created_by_application?
attribute :invited_by_account_id, if: :invited?
@@ -21,10 +21,18 @@
delegate :website, to: :object
+ def deleted
+ object.deleted?
+ end
+
def email
object.user_email
end
+ def sms
+ object.user_sms
+ end
+
def ip
object.user_current_sign_in_ip.to_s.presence
end
@@ -79,5 +87,17 @@
def created_by_application?
object.user&.created_by_application_id&.present?
+ end
+
+ def sms_reverification_required
+ !!object.user&.user_sms_reverification_required&.user_id
+ end
+
+ def updated_at
+ object.updated_at
+ end
+
+ def advertiser
+ !!object.recent_ads.presence
end
end
Only in truth-new/opensource/app/serializers/rest/admin: chat_message_serializer.rb
Only in truth-new/opensource/app/serializers/rest/admin: one_time_challenge_serializer.rb
Only in truth-new/opensource/app/serializers/rest/admin: tag_search_serializer.rb
Only in truth-new/opensource/app/serializers/rest/admin: tag_serializer.rb
Only in truth-new/opensource/app/serializers/rest/admin: webauthn_credential_serializer.rb
Only in truth-new/opensource/app/serializers/rest: avatars_carousel_serializer.rb
Only in truth-new/opensource/app/serializers/rest: chat_member_removal_serializer.rb
Only in truth-new/opensource/app/serializers/rest: chat_message_serializer.rb
Only in truth-new/opensource/app/serializers/rest: chat_serializer.rb
Only in truth-new/opensource/app/serializers/rest: chat_silence_serializer.rb
diff -ru truth-old/opensource/app/serializers/rest/credential_account_serializer.rb truth-new/opensource/app/serializers/rest/credential_account_serializer.rb
--- truth-old/opensource/app/serializers/rest/credential_account_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/credential_account_serializer.rb 2024-04-05 09:09:43
@@ -1,11 +1,12 @@
# frozen_string_literal: true
class REST::CredentialAccountSerializer < REST::AccountSerializer
- attributes :source, :pleroma
+ attributes :source, :pleroma, :features
+ SMS_REVERIFICATION_DEADLINE = 90
def source
user = object.user
- waitlist_enabled = ENV.fetch("WAITLIST_ENABLED", "true")
+ waitlist_enabled = ENV.fetch('WAITLIST_ENABLED', 'true')
source = {
privacy: user.setting_default_privacy,
@@ -18,17 +19,45 @@
sms_verified: (user.not_ready_for_approval? || user.ready_by_csv_import? || user.sms_verified?),
ready_by_sms_verification: (!user.not_ready_for_approval? && !user.ready_by_csv_import?),
follow_requests_count: FollowRequest.where(target_account: object).limit(40).count,
+ accepting_messages: object.accepting_messages,
+ chats_onboarded: true,
+ feeds_onboarded: object.feeds_onboarded,
+ tv_onboarded: object.tv_onboarded,
+ show_nonmember_group_statuses: object.show_nonmember_group_statuses,
+ unauth_visibility: !!user.unauth_visibility,
+ integrity: user.integrity_score,
+ integrity_status: user.integrity_status(instance_options[:access_token], instance_options[:android_client]),
+ sms_reverification_required: !!user.user_sms_reverification_required&.user_id,
+ sms: user.sms.present?,
+ sms_country: user.sms_country,
+ receive_only_follow_mentions: object.receive_only_follow_mentions
}
- source[:unapproved_position] = user.get_position_in_waitlist_queue if waitlist_enabled == "true"
- return source
+ source[:unapproved_position] = user.get_position_in_waitlist_queue if waitlist_enabled == 'true'
+ source[:sms_last_four_digits] = user.sms.last(4) if user.sms.present?
+ source[:sms_reverification_days_left] = sms_reverification_days_left(user) if user.user_sms_reverification_required&.user_id
+ source
end
+ def sms_reverification_days_left(user)
+ action_date = Admin::ActionLog.select(:created_at).where(target_type: 'User', target_id: user.id, action: 'enable_sms_reverification').order('created_at DESC').first&.created_at
+ return SMS_REVERIFICATION_DEADLINE unless action_date
+ [SMS_REVERIFICATION_DEADLINE - ((Time.now - action_date) / 1.day).round, 0].max
+ end
+
def pleroma
{
+ accepts_chat_messages: object.accepting_messages,
settings_store: object.settings_store,
- is_admin: object.user.admin,
- is_moderator: object.user.moderator
}
+ end
+
+ def features
+ enabled_features = object.feature_flags.pluck(:name)
+
+ ::Configuration::FeatureFlag.all.each_with_object({}) do |feature, hash|
+ name = feature.name
+ hash[name] = feature.enabled? || feature.account_based? && enabled_features.include?(name)
+ end
end
end
Only in truth-new/opensource/app/serializers/rest: feed_serializer.rb
Only in truth-new/opensource/app/serializers/rest: group_membership_serializer.rb
Only in truth-new/opensource/app/serializers/rest: group_relationship_serializer.rb
Only in truth-new/opensource/app/serializers/rest: group_serializer.rb
Only in truth-new/opensource/app/serializers/rest: group_suggestion_serializer.rb
Only in truth-new/opensource/app/serializers/rest: groups_avatar_serializer.rb
Only in truth-new/opensource/app/serializers/rest: groups_carousel_serializer.rb
diff -ru truth-old/opensource/app/serializers/rest/instance_serializer.rb truth-new/opensource/app/serializers/rest/instance_serializer.rb
--- truth-old/opensource/app/serializers/rest/instance_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/instance_serializer.rb 2024-04-01 14:59:13
@@ -6,12 +6,8 @@
attributes :uri, :title, :short_description, :description, :email,
:version, :urls, :thumbnail, :languages, :registrations,
:approval_required, :invites_enabled, :configuration,
- :feature_quote
+ :feature_quote, :rules
- has_many :rules, serializer: REST::RuleSerializer
-
- delegate :rules, to: :instance_presenter
-
def uri
Rails.configuration.x.local_domain
end
@@ -33,7 +29,8 @@
end
def version
- "#{Mastodon::Version} (compatible; TruthSocial 1.0.0)"
+ is_staging = ActiveModel::Type::Boolean.new.cast(ENV['IS_STAGING'])
+ "#{Mastodon::Version} (compatible; TruthSocial 1.0.0#{is_staging ? '+unreleased' : ''})"
end
def thumbnail
@@ -45,20 +42,29 @@
end
def configuration
+ ads_configuration = JSON.parse(ENV.fetch('ADS_CONFIGURATION', '[{}]'))
+
{
statuses: {
max_characters: StatusLengthValidator::MAX_CHARS,
max_media_attachments: 4,
- characters_reserved_per_url: StatusLengthValidator::URL_PLACEHOLDER_CHARS,
+ characters_reserved_per_url: URLPlaceholder::LENGTH,
},
+ chats: {
+ max_characters: ChatMessage::MAX_CHARS,
+ max_messages_per_minute: ChatMessage::MAX_MESSAGES_PER_MIN,
+ max_media_attachments: ENV.fetch('MAX_ATTACHMENTS_ALLOWED_PER_MESSAGE', 4).to_i,
+ },
+
media_attachments: {
- supported_mime_types: MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES + MediaAttachment::AUDIO_MIME_TYPES,
+ supported_mime_types: MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES,
image_size_limit: MediaAttachment::IMAGE_LIMIT,
image_matrix_limit: Attachmentable::MAX_MATRIX_LIMIT,
video_size_limit: MediaAttachment::VIDEO_LIMIT,
video_frame_rate_limit: MediaAttachment::MAX_VIDEO_FRAME_RATE,
video_matrix_limit: MediaAttachment::MAX_VIDEO_MATRIX_LIMIT,
+ video_duration_limit: MediaAttachment::MAX_VIDEO_DURATION_LIMIT,
},
polls: {
@@ -67,6 +73,22 @@
min_expiration: PollValidator::MIN_EXPIRATION,
max_expiration: PollValidator::MAX_EXPIRATION,
},
+
+ ads: {
+ algorithm: {
+ name: ads_configuration[0]&.[]('value'),
+ configuration: {
+ frequency: ads_configuration[1]&.[]('value').to_i,
+ phase_min: ads_configuration[2]&.[]('value').to_f,
+ phase_max: ads_configuration[3]&.[]('value').to_f,
+ },
+ },
+ },
+ groups: {
+ max_characters_name: ENV.fetch('MAX_GROUP_NAME_CHARS', 35).to_i,
+ max_characters_description: ENV.fetch('MAX_GROUP_NOTE_CHARS', 160).to_i,
+ max_admins_allowed: ENV.fetch('MAX_GROUP_ADMINS_ALLOWED', 10).to_i,
+ },
}
end
@@ -87,7 +109,11 @@
end
def feature_quote
- false
+ true
+ end
+
+ def rules
+ ActiveModelSerializers::SerializableResource.new(Rule.ordered, each_serializer: REST::RuleSerializer).as_json
end
private
diff -ru truth-old/opensource/app/serializers/rest/media_attachment_serializer.rb truth-new/opensource/app/serializers/rest/media_attachment_serializer.rb
--- truth-old/opensource/app/serializers/rest/media_attachment_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/media_attachment_serializer.rb 2024-04-01 14:59:13
@@ -5,14 +5,14 @@
attributes :id, :type, :url, :preview_url, :external_video_id,
:remote_url, :preview_remote_url, :text_url, :meta,
- :description, :blurhash
+ :description, :blurhash, :tv
def id
object.id.to_s
end
def url
- if object.not_processed?
+ if object.type != 'video' && object.not_processed?
nil
elsif object.needs_redownload?
media_proxy_url(object.id, :original)
@@ -26,8 +26,8 @@
end
def preview_url
- if object.type == "video"
- #TODO: replace the image and upload it to CDN
+ if object.type == 'video'
+ # TODO: replace the image and upload it to CDN
object.external_video_id && object.status.preview_card&.image? ? full_asset_url(object.status.preview_card.image.url(:original)) : full_asset_url('/icons/missing.png')
elsif object.needs_redownload?
media_proxy_url(object.id, :small)
@@ -48,5 +48,14 @@
def meta
object.file.meta
+ end
+
+ def external_video_id
+ return nil if object.not_processed?
+ object.external_video_id
+ end
+
+ def tv
+ REST::TvProgramSerializer.new(instance_options[:tv_program]) if instance_options && instance_options[:tv_program]
end
end
diff -ru truth-old/opensource/app/serializers/rest/notification_serializer.rb truth-new/opensource/app/serializers/rest/notification_serializer.rb
--- truth-old/opensource/app/serializers/rest/notification_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/notification_serializer.rb 2024-04-01 14:59:13
@@ -3,9 +3,10 @@
class REST::NotificationSerializer < ActiveModel::Serializer
attributes :id, :type, :total_count, :created_at
attribute :total_count, if: -> { object.count.present? }
+ attribute :target_chat_message, if: -> { object.type == :chat || object.type == :chat_message_deleted }
+ attribute :target_status, key: :status, if: :status_type?
belongs_to :from_account, key: :account, serializer: REST::AccountSerializer
- belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
def id
object.id.to_s
@@ -19,7 +20,26 @@
object.count
end
+ def target_status
+ REST::V2::StatusSerializer.new(context: { current_user: instance_options[:current_user] }).serialize(object.target_status) if object.target_status
+ end
+
def status_type?
- [:favourite, :favourite_group, :reblog, :reblog_group, :status, :mention, :mention_group, :poll].include?(object.type)
+ [:favourite, :favourite_group, :group_favourite, :group_favourite_group,
+ :reblog, :reblog_group, :group_reblog, :group_reblog_group,
+ :status,
+ :mention, :mention_group, :group_mention, :group_mention_group,
+ :poll].include?(object.type)
+ end
+
+ def target_chat_message
+ chat_message = ChatMessage.find_message(object.account_id, object.activity.chat_id, object.activity_id)
+
+ if chat_message
+ decoded = ActiveSupport::JSON.decode(chat_message)
+ chat_message_obj = ChatMessage.new(decoded)
+
+ ActiveModelSerializers::SerializableResource.new(chat_message_obj, serializer: REST::ChatMessageSerializer).as_json
+ end
end
end
Only in truth-new/opensource/app/serializers/rest: oauth_token_serializer.rb
diff -ru truth-old/opensource/app/serializers/rest/preview_card_serializer.rb truth-new/opensource/app/serializers/rest/preview_card_serializer.rb
--- truth-old/opensource/app/serializers/rest/preview_card_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/preview_card_serializer.rb 2024-04-01 14:59:13
@@ -8,7 +8,37 @@
:provider_url, :html, :width, :height,
:image, :embed_url, :blurhash
+ attribute :group, if: -> { instance_options && instance_options[:group] }
+
def image
object.image? ? full_asset_url(object.image.url(:original)) : nil
+ end
+
+ def url
+ url = object.url
+
+ if instance_options && instance_options[:external_links]
+ links = instance_options[:external_links].index_by(&:url)
+ if (link_id = links[object&.url]&.id)
+ url = link_url(link_id, subdomain: 'links')
+ end
+ end
+ url
+ end
+
+ def provider_name
+ url = object.provider_name
+ if url.blank?
+ url = Addressable::URI.parse(object.url)&.host || ''
+ end
+ url
+ end
+
+ def group
+ REST::GroupSerializer.new(instance_options[:group])
+ end
+
+ def html
+ Sanitize.fragment(object.html, Sanitize::Config::MASTODON_OEMBED)
end
end
Only in truth-new/opensource/app/serializers/rest: revcontent_ad_serializer.rb
Only in truth-new/opensource/app/serializers/rest: revcontent_ads_serializer.rb
diff -ru truth-old/opensource/app/serializers/rest/rule_serializer.rb truth-new/opensource/app/serializers/rest/rule_serializer.rb
--- truth-old/opensource/app/serializers/rest/rule_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/rule_serializer.rb 2024-04-01 14:59:13
@@ -6,4 +6,16 @@
def id
object.id.to_s
end
+
+ def rule_type
+ object.rule_type == "rule_type_group" ? :group : object.rule_type
+ end
+
+ def text
+ I18n.t("admin.instances.rules.#{object.name}.text")
+ end
+
+ def subtext
+ I18n.t("admin.instances.rules.#{object.name}.subtext")
+ end
end
diff -ru truth-old/opensource/app/serializers/rest/search_serializer.rb truth-new/opensource/app/serializers/rest/search_serializer.rb
--- truth-old/opensource/app/serializers/rest/search_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/search_serializer.rb 2024-04-12 09:09:08
@@ -1,7 +1,14 @@
# frozen_string_literal: true
class REST::SearchSerializer < ActiveModel::Serializer
- has_many :accounts, serializer: REST::AccountSerializer
+ attributes :hashtags, :accounts
+
has_many :statuses, serializer: REST::StatusSerializer
- has_many :hashtags, serializer: REST::TagSerializer
+ delegate :hashtags, to: :object
+ has_many :groups, serializer: REST::GroupSerializer
+
+ def accounts
+ ActiveModel::SerializableResource.new(object.accounts, each_serializer: REST::AccountSerializer, tv_account_lookup: true)
+ end
+
end
diff -ru truth-old/opensource/app/serializers/rest/status_serializer.rb truth-new/opensource/app/serializers/rest/status_serializer.rb
--- truth-old/opensource/app/serializers/rest/status_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/rest/status_serializer.rb 2024-04-01 14:59:13
@@ -3,11 +3,12 @@
class REST::StatusSerializer < ActiveModel::Serializer
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
:sensitive, :spoiler_text, :visibility, :language,
- :uri, :url
+ :uri, :url, :sponsored, :tombstone, :tv
attribute :replies_count
attribute :reblogs_count
attribute :favourites_count
+ attribute :group_timeline_visible
attribute :favourited, if: :current_user?
attribute :reblogged, if: :current_user?
attribute :muted, if: :current_user?
@@ -18,17 +19,19 @@
attribute :text, if: :source_requested?
attribute :quote_id, if: -> { object.quote? }
+ attribute :metrics, if: -> { object.ad.present? }
+ attribute :preview_card, key: :card
+ attribute :media_attachments
belongs_to :reblog, serializer: REST::StatusSerializer
belongs_to :application, if: :show_application?
belongs_to :account, serializer: REST::AccountSerializer
+ belongs_to :group, serializer: REST::GroupSerializer
- has_many :media_attachments, serializer: REST::MediaAttachmentSerializer
has_many :ordered_mentions, key: :mentions
has_many :tags
has_many :emojis, serializer: REST::CustomEmojiSerializer
- has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer
has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer
def id
@@ -47,6 +50,20 @@
object.quote_id.to_s
end
+ def metrics
+ REST::AdMetricSerializer.new.serialize(object.ad)
+ end
+
+ def preview_card
+ group = if instance_options && instance_options[:relationships]
+ instance_options[:relationships].groups_map[object.id] || false
+ else
+ Status.groups_map([object])[object.id] || false
+ end
+
+ REST::PreviewCardSerializer.new(object.preview_card, external_links: object.links, group: group) if object.preview_card
+ end
+
def current_user?
if defined?(current_user)
!current_user.nil?
@@ -81,7 +98,7 @@
end
def content
- Formatter.instance.format(object)
+ Formatter.instance.format(object, { external_links: object.links })
end
def url
@@ -159,6 +176,24 @@
object.active_mentions.to_a.sort_by(&:id)
end
+ def sponsored
+ !!object.ad
+ end
+
+ def tombstone
+ nil
+ end
+
+ def media_attachments
+ object.media_attachments.map do |attachment|
+ REST::MediaAttachmentSerializer.new(attachment, tv_program: object.tv_program)
+ end
+ end
+
+ def tv
+ REST::TvProgramSerializer.new(object.tv_program) if object.tv_program
+ end
+
class ApplicationSerializer < ActiveModel::Serializer
attributes :name, :website
end
@@ -174,6 +209,11 @@
object.account_username
end
+ def group_timeline_visible
+ object.group ? object.group_timeline_visible : true
+ end
+
+
def url
ActivityPub::TagManager.instance.url_for(object.account)
end
@@ -204,7 +244,7 @@
if instance_options && instance_options[:account_relationships]
instance_options[:account_relationships].muting[object.account_id] ? true : false || instance_options[:account_relationships].blocking[object.account_id] || instance_options[:account_relationships].blocked_by[object.account_id] || instance_options[:account_relationships].domain_blocking[object.account_id] || false
else
- current_user.account.muting?(object.account) || object.account.blocking?(current_user.account) || current_user.account.blocking?(object.account) || current_user.account.domain_blocking?(object.account.domain)
+ current_user.account.muting?(object.account) || object.account.blocking?(current_user.account) || current_user.account.blocking?(object.account) || current_user.account.domain_blocking?(object.account.domain)
end
end
end
@@ -228,6 +268,6 @@
end
class REST::StatusSerializer < ActiveModel::Serializer
- belongs_to :quote, serializer: REST::NestedQuoteSerializer
- belongs_to :thread, serializer: REST::InReplySerializer, key: :in_reply_to, if: -> { !@instance_options[:exclude_reply_previews] }
+ belongs_to :quote, serializer: REST::NestedQuoteSerializer, if: -> { (object&.quote&.visibility != 'self' || (current_user? && current_user.account_id == object&.quote&.account_id)) }
+ belongs_to :thread, serializer: REST::InReplySerializer, key: :in_reply_to, if: -> { !@instance_options[:exclude_reply_previews] && (object&.thread&.visibility != 'self' || (current_user? && current_user.account_id == object&.thread&.account_id)) }
end
Only in truth-new/opensource/app/serializers/rest: suggestions_carousel_serializer.rb
Only in truth-new/opensource/app/serializers/rest: truth
Only in truth-new/opensource/app/serializers/rest: tv_program_serializer.rb
Only in truth-new/opensource/app/serializers/rest: v2
diff -ru truth-old/opensource/app/serializers/web/notification_serializer.rb truth-new/opensource/app/serializers/web/notification_serializer.rb
--- truth-old/opensource/app/serializers/web/notification_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/web/notification_serializer.rb 2023-05-05 13:42:02
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Web::NotificationSerializer < ActiveModel::Serializer
+class Web::NotificationSerializer < NotificationSerializer
include RoutingHelper
include ActionView::Helpers::TextHelper
include ActionView::Helpers::SanitizeHelper
@@ -29,7 +29,7 @@
end
def title
- I18n.t("notification_mailer.#{object.type}.subject", name: object.from_account.display_name.presence || object.from_account.username)
+ I18n.t("notification_mailer.#{template}.subject", mailer_params)
end
def body
diff -ru truth-old/opensource/app/serializers/webfinger_serializer.rb truth-new/opensource/app/serializers/webfinger_serializer.rb
--- truth-old/opensource/app/serializers/webfinger_serializer.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/serializers/webfinger_serializer.rb 2024-04-01 14:59:13
@@ -10,25 +10,14 @@
end
def aliases
- if object.instance_actor?
- [instance_actor_url]
- else
- [short_account_url(object), account_url(object)]
- end
+ [short_account_url(object), account_url(object)]
end
def links
- if object.instance_actor?
- [
- { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: about_more_url(instance_actor: true) },
- { rel: 'self', type: 'application/activity+json', href: instance_actor_url },
- ]
- else
- [
- { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) },
- { rel: 'self', type: 'application/activity+json', href: account_url(object) },
- { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" },
- ]
- end
+ [
+ { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) },
+ { rel: 'self', type: 'application/activity+json', href: account_url(object) },
+ # { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" },
+ ]
end
end
diff -ru truth-old/opensource/app/services/account_search_service.rb truth-new/opensource/app/services/account_search_service.rb
--- truth-old/opensource/app/services/account_search_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/account_search_service.rb 2024-04-01 14:59:13
@@ -17,7 +17,8 @@
private
def search_service_results
- return [] if query.blank? || limit < 1
+ return [] if limit < 1
+ return [] if query.blank? && !options[:followers]
[exact_match] + search_results
end
@@ -30,6 +31,7 @@
match = Account.find_local(query)
match = nil if !match.nil? && !account.nil? && options[:following] && !account.following?(match)
+ match = nil if options[:followers] && !match&.following?(account)
@exact_match = match
end
@@ -38,7 +40,7 @@
return [] if limit_for_non_exact_results.zero?
@search_results ||= begin
- results = from_elasticsearch if Chewy.enabled?
+ results = from_elasticsearch if Chewy.enabled? && !options[:followers]
results ||= from_database
results
end
@@ -57,14 +59,20 @@
end
def advanced_search_results
- Account.advanced_search_for(query, account, limit_for_non_exact_results, options[:following], offset)
+ Account.advanced_search_for(query, account, limit, options[:following], offset)
end
def follower_search
- account.followers.where('LOWER(username) LIKE ?', '%' + query.downcase + '%').limit(20)
+ account
+ .followers_unordered
+ .where('LOWER(username) LIKE :search OR LOWER(display_name) LIKE :search', search: "%#{sanitize_search(query&.downcase)}%")
+ .where(accepting_messages: true)
+ .limit(limit)
+ .offset(offset)
+ .order(username: :asc)
end
- def fields
+ def fields
if likely_username?
%w(acct.edge_ngram acct)
elsif likely_display_name?
@@ -89,14 +97,23 @@
functions = [reputation_score_function, followers_score_function, time_distance_function]
- records = AccountsIndex.query(function_score: { query: fields_query, functions: functions, boost_mode: 'multiply', score_mode: 'multiply' })
+ records = AccountsIndex.query(
+ function_score: {
+ query: fields_query,
+ functions: functions,
+ boost_mode: 'multiply',
+ score_mode: 'multiply',
+ }
+ )
.filter(SearchService::PROHIBITED_FILTERS)
+ .filter(term: { suspended: false })
+ .filter(exists: { field: 'email' })
.limit(limit_for_non_exact_results)
.offset(offset)
.objects
.compact
- ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
+ ActiveRecord::Associations::Preloader.new.preload(records, [:account_follower, :account_following, :account_status, :tv_channel_account, :moved_to_account])
records
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
@@ -168,4 +185,7 @@
@acct_hint
end
+ def sanitize_search(query)
+ ActiveRecord::Base.sanitize_sql_like(query || '')
+ end
end
Only in truth-old/opensource/app/services/activitypub: prepare_followers_synchronization_service.rb
Only in truth-old/opensource/app/services/activitypub: process_collection_service.rb
diff -ru truth-old/opensource/app/services/activitypub/process_poll_service.rb truth-new/opensource/app/services/activitypub/process_poll_service.rb
--- truth-old/opensource/app/services/activitypub/process_poll_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/activitypub/process_poll_service.rb 2024-04-12 09:09:08
@@ -8,7 +8,7 @@
return unless expected_type?
- previous_expires_at = poll.expires_at
+ #previous_expires_at = poll.expires_at
expires_at = if @json['closed'].is_a?(String)
@json['closed']
@@ -47,9 +47,9 @@
# If the poll had no expiration date set but now has, and people have voted,
# schedule a notification.
- if previous_expires_at.nil? && poll.expires_at.present? && poll.votes.exists?
- PollExpirationNotifyWorker.perform_at(poll.expires_at + 5.minutes, poll.id)
- end
+ # if previous_expires_at.nil? && poll.expires_at.present? && poll.votes.exists?
+ # PollExpirationNotifyWorker.perform_at(poll.expires_at + 5.minutes, poll.id)
+ # end
end
private
Only in truth-new/opensource/app/services: admin_account_search_service.rb
Only in truth-new/opensource/app/services: ads_service.rb
diff -ru truth-old/opensource/app/services/after_unallow_domain_service.rb truth-new/opensource/app/services/after_unallow_domain_service.rb
--- truth-old/opensource/app/services/after_unallow_domain_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/after_unallow_domain_service.rb 2024-04-01 14:59:13
@@ -3,7 +3,13 @@
class AfterUnallowDomainService < BaseService
def call(domain)
Account.where(domain: domain).find_each do |account|
- DeleteAccountService.new.call(account, reserve_username: false)
+ DeleteAccountService.new.call(
+ account,
+ DeleteAccountService::DELETED_BY_SERVICE,
+ deletion_type: 'service_unallowed_domain',
+ reserve_username: false,
+ skip_activitypub: true,
+ )
end
end
end
Only in truth-new/opensource/app/services: android_device_check
diff -ru truth-old/opensource/app/services/app_sign_up_service.rb truth-new/opensource/app/services/app_sign_up_service.rb
--- truth-old/opensource/app/services/app_sign_up_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/app_sign_up_service.rb 2024-04-01 14:59:13
@@ -11,7 +11,7 @@
invite_request_params = { text: params[:reason] }
user = User.create!(user_params.merge(created_by_application: app, sign_up_ip: remote_ip, password_confirmation: user_params[:password], account_attributes: account_params, invite_request_attributes: invite_request_params))
- Doorkeeper::AccessToken.create!(application: app,
+ OauthAccessToken.create!(application: app,
resource_owner_id: user.id,
scopes: app.scopes,
expires_in: Doorkeeper.configuration.access_token_expires_in,
Only in truth-new/opensource/app/services: assertion_service.rb
Only in truth-new/opensource/app/services: authorize_membership_service.rb
diff -ru truth-old/opensource/app/services/batched_remove_status_service.rb truth-new/opensource/app/services/batched_remove_status_service.rb
--- truth-old/opensource/app/services/batched_remove_status_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/batched_remove_status_service.rb 2023-05-05 13:42:02
@@ -31,7 +31,7 @@
# Since we skipped all callbacks, we also need to manually
# deindex the statuses
- Chewy.strategy.current.update(StatusesIndex::Status, statuses_and_reblogs) if Chewy.enabled?
+ Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) if Chewy.enabled?
return if options[:skip_side_effects]
diff -ru truth-old/opensource/app/services/block_domain_service.rb truth-new/opensource/app/services/block_domain_service.rb
--- truth-old/opensource/app/services/block_domain_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/block_domain_service.rb 2024-04-01 14:59:13
@@ -37,7 +37,14 @@
blocked_domain_accounts.without_suspended.in_batches.update_all(suspended_at: @domain_block.created_at, suspension_origin: :local)
blocked_domain_accounts.where(suspended_at: @domain_block.created_at).reorder(nil).find_each do |account|
- DeleteAccountService.new.call(account, reserve_username: true, suspended_at: @domain_block.created_at)
+ DeleteAccountService.new.call(
+ account,
+ DeleteAccountService::DELETED_BY_SERVICE,
+ deletion_type: 'service_block_domain',
+ reserve_username: true,
+ skip_activitypub: true,
+ suspended_at: @domain_block.created_at,
+ )
end
end
diff -ru truth-old/opensource/app/services/block_service.rb truth-new/opensource/app/services/block_service.rb
--- truth-old/opensource/app/services/block_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/block_service.rb 2024-04-01 14:59:13
@@ -6,12 +6,20 @@
def call(account, target_account)
return if account.id == target_account.id
- UnfollowService.new.call(account, target_account) if account.following?(target_account)
- UnfollowService.new.call(target_account, account) if target_account.following?(account)
+ if account.following?(target_account)
+ UnfollowService.new.call(account, target_account)
+ FollowDelete.where(account_id: account.id).destroy_all
+ end
+
+ if target_account.following?(account)
+ UnfollowService.new.call(target_account, account)
+ FollowDelete.where(target_account_id: target_account).destroy_all
+ end
+
RejectFollowService.new.call(target_account, account) if target_account.requested?(account)
block = account.block!(target_account)
-
+ invalidate_secondary_caches(account, target_account)
BlockWorker.perform_async(account.id, target_account.id)
create_notification(block) if !target_account.local? && target_account.activitypub?
export_prometheus_metric
@@ -19,6 +27,11 @@
end
private
+
+ def invalidate_secondary_caches(account, target_account)
+ InvalidateSecondaryCacheService.new.call("InvalidateFollowCacheWorker", account.id, target_account.id, target_account.whale?)
+ InvalidateSecondaryCacheService.new.call("InvalidateFollowCacheWorker", target_account.id, account.id, account.whale?)
+ end
def create_notification(block)
ActivityPub::DeliveryWorker.perform_async(build_json(block), block.account_id, block.target_account.inbox_url)
Only in truth-new/opensource/app/services: canonical_request_service.rb
Only in truth-new/opensource/app/services: chat_message_reaction_service.rb
Only in truth-new/opensource/app/services: chat_message_service.rb
Only in truth-new/opensource/app/services: chat_service.rb
Only in truth-new/opensource/app/services/concerns: app_attestable.rb
Only in truth-new/opensource/app/services/concerns: challengeable.rb
Only in truth-new/opensource/app/services/concerns: group_cachable.rb
Only in truth-new/opensource/app/services/concerns: links_parser_concern.rb
Only in truth-new/opensource/app/services/concerns: upload_video_concern.rb
diff -ru truth-old/opensource/app/services/delete_account_service.rb truth-new/opensource/app/services/delete_account_service.rb
--- truth-old/opensource/app/services/delete_account_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/delete_account_service.rb 2024-04-01 14:59:13
@@ -1,8 +1,18 @@
# frozen_string_literal: true
+class UnknownDeletionType < StandardError; end
+
class DeleteAccountService < BaseService
include Payloadable
+ # This can be passed to #call as the deleted_by_id to indicate that
+ # this account was deleted by an automated process, rather than by
+ # a user or moderator. This is then passed to Janus.
+ #
+ # NOTE: The deletion_type option should be used to give a more specific
+ # details on how the account was deleted.
+ DELETED_BY_SERVICE = -99
+
ASSOCIATIONS_ON_SUSPEND = %w(
account_pins
active_relationships
@@ -72,9 +82,15 @@
# @option [Boolean] :skip_side_effects Side effects are ActivityPub and streaming API payloads
# @option [Boolean] :skip_activitypub Skip sending ActivityPub payloads. Implied by :skip_side_effects
# @option [Time] :suspended_at Only applicable when :reserve_username is true
- def call(account, **options)
+ def call(account, deleted_by_id, **options)
@account = account
- @options = { reserve_username: true, reserve_email: true }.merge(options)
+ @deleted_by_id = deleted_by_id
+ defaults = {
+ deletion_type: 'unknown',
+ reserve_email: true,
+ reserve_username: true,
+ }
+ @options = defaults.merge(options)
if @account.local? && @account.user_unconfirmed_or_pending?
@options[:reserve_email] = false
@@ -87,10 +103,34 @@
distribute_activities!
purge_content!
fulfill_deletion_request!
+ publish_delete_event
+ track_account_deletion
end
private
+ def track_account_deletion
+ Logs::AccountDeletion.create!(
+ account_id: @account.id,
+ user_id: @account&.user&.id,
+ username: @account.username,
+ email: @account&.user_email,
+ deleted_at: Time.now.utc,
+ account_deletion_type: @options[:deletion_type],
+ deleted_by_account_id: @deleted_by_id,
+ )
+ if @options[:deletion_type] == 'unknown'
+ raise UnknownDeletionType
+ end
+ rescue UnknownDeletionType => e
+ Rails.logger.warn('Track account deletion: Unknown deletion type')
+ Rails.logger.warn(e.backtrace.join("\n"))
+ rescue ActiveRecord::NotNullViolation => e
+ Rails.logger.info("Failed to track account deletion: #{e.message}")
+ rescue ActiveRecord::RecordNotUnique => e
+ Rails.logger.info("Failed to track account deletion, account_id: #{@account.id}, #{e.message}")
+ end
+
def distribute_activities!
return if skip_activitypub?
@@ -173,21 +213,22 @@
end
def purge_polls!
- @account.polls.reorder(nil).where.not(status_id: reported_status_ids).in_batches.delete_all
+ @account.polls.reorder(nil).merge(Status.where.not(id: reported_status_ids)).in_batches.delete_all
end
def purge_generated_notifications!
# By deleting polls and statuses without callbacks, we've left behind
# polymorphically associated notifications generated by this account
- Notification.where(from_account: @account).in_batches.delete_all
+ Notification.where(from_account: @account).in_batches do |batch|
+ batch.delete_all
+ end
end
def purge_favourites!
@account.favourites.in_batches do |favourites|
ids = favourites.pluck(:status_id)
- StatusStat.where(status_id: ids).update_all('favourites_count = GREATEST(0, favourites_count - 1)')
- Chewy.strategy.current.update(StatusesIndex::Status, ids) if Chewy.enabled?
+ Chewy.strategy.current.update(StatusesIndex, ids) if Chewy.enabled?
Rails.cache.delete_multi(ids.map { |id| "statuses/#{id}" })
favourites.delete_all
end
@@ -195,7 +236,7 @@
def purge_bookmarks!
@account.bookmarks.in_batches do |bookmarks|
- Chewy.strategy.current.update(StatusesIndex::Status, bookmarks.pluck(:status_id)) if Chewy.enabled?
+ Chewy.strategy.current.update(StatusesIndex, bookmarks.pluck(:status_id)) if Chewy.enabled?
bookmarks.delete_all
end
end
@@ -229,9 +270,6 @@
@account.display_name = ''
@account.note = ''
@account.fields = []
- @account.statuses_count = 0
- @account.followers_count = 0
- @account.following_count = 0
@account.moved_to_account = nil
@account.also_known_as = []
@account.trust_level = :untrusted
@@ -302,5 +340,9 @@
def skip_activitypub?
@options[:skip_activitypub]
+ end
+
+ def publish_delete_event
+ EventProvider::EventProvider.new('account.deleted', AccountDeletedEvent, {account_id: @account.id, deleted_by_id: @deleted_by_id}).call
end
end
Only in truth-new/opensource/app/services: delete_group_service.rb
Only in truth-new/opensource/app/services: destroy_group_service.rb
Only in truth-new/opensource/app/services: disabled_user_refollow_service.rb
Only in truth-new/opensource/app/services: disabled_user_unfollow_service.rb
diff -ru truth-old/opensource/app/services/fan_out_on_write_service.rb truth-new/opensource/app/services/fan_out_on_write_service.rb
--- truth-old/opensource/app/services/fan_out_on_write_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/fan_out_on_write_service.rb 2024-04-01 14:59:13
@@ -25,7 +25,7 @@
deliver_to_hashtags(status)
- return if status.reply? && status.in_reply_to_account_id != status.account_id
+ nil if status.reply? && status.in_reply_to_account_id != status.account_id
end
@@ -73,8 +73,7 @@
end
def render_anonymous_payload(status)
- @payload = InlineRenderer.render(status, nil, :status)
- @payload = Oj.dump(event: :update, payload: @payload)
+ @payload = REST::V2::StatusSerializer.new.serialize_to_json(status)
end
def deliver_to_hashtags(status)
diff -ru truth-old/opensource/app/services/favourite_service.rb truth-new/opensource/app/services/favourite_service.rb
--- truth-old/opensource/app/services/favourite_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/favourite_service.rb 2024-04-01 14:59:13
@@ -3,18 +3,32 @@
class FavouriteService < BaseService
include Authorization
include Payloadable
+ include Redisable
+ DUPLICATE_FAVOURITE_EXPIRE_AFTER = 7.days.seconds
+
# Favourite a status and notify remote user
# @param [Account] account
# @param [Status] status
# @return [Favourite]
- def call(account, status)
+ def call(account, status, options = {})
authorize_with account, status, :favourite?
favourite = Favourite.find_by(account: account, status: status)
- return favourite unless favourite.nil?
+ unless favourite.nil?
+ if options[:user_agent]
+ redis_key = "duplicate_favourites:#{DateTime.current.to_date}"
+ redis_element_key = options[:user_agent]
+ redis.zincrby(redis_key, 1, redis_element_key)
+ redis.expire(redis_key, DUPLICATE_FAVOURITE_EXPIRE_AFTER)
+ Rails.logger.error "duplicate_favourites: #{favourite.id}, difference: #{Time.now.to_i - favourite.created_at.to_i}, user_agent: #{redis_element_key}"
+ end
+
+ return favourite
+ end
+
favourite = Favourite.create!(account: account, status: status)
read_from_replica do
@@ -31,9 +45,10 @@
def create_notification(favourite)
status = favourite.status
+ type = status.group ? :group_favourite : :favourite
if status.account.local?
- NotifyService.new.call(status.account, :favourite, favourite)
+ NotifyService.new.call(status.account, type, favourite)
elsif status.account.activitypub?
ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url)
end
@@ -41,8 +56,7 @@
def bump_potential_friendship(account, status)
ActivityTracker.increment('activity:interactions')
- return if account.following?(status.account_id)
- PotentialFriendshipTracker.record(account.id, status.account_id, :favourite)
+ InteractionsTracker.new(account.id, status.account_id, :favourite, account.following?(status.account_id), status.group).track
end
def build_json(favourite)
diff -ru truth-old/opensource/app/services/fetch_link_card_service.rb truth-new/opensource/app/services/fetch_link_card_service.rb
--- truth-old/opensource/app/services/fetch_link_card_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/fetch_link_card_service.rb 2024-04-01 14:59:13
@@ -1,21 +1,12 @@
# frozen_string_literal: true
-require "./lib/proto/serializers/card_joined_event.rb"
class FetchLinkCardService < BaseService
- URL_PATTERN = %r{
- (#{Twitter::TwitterText::Regex[:valid_url_preceding_chars]}) # $1 preceeding chars
- ( # $2 URL
- (https?:\/\/) # $3 Protocol (required)
- (#{Twitter::TwitterText::Regex[:valid_domain]}) # $4 Domain(s)
- (?::(#{Twitter::TwitterText::Regex[:valid_port_number]}))? # $5 Port number (optional)
- (/#{Twitter::TwitterText::Regex[:valid_url_path]}*)? # $6 URL Path and anchor
- (\?#{Twitter::TwitterText::Regex[:valid_url_query_chars]}*#{Twitter::TwitterText::Regex[:valid_url_query_ending_chars]})? # $7 Query String
- )
- }iox
+ include LinksParserConcern
- def call(status, url = nil)
+ def call(status, url = nil, request_domain = nil)
@status = status
- @url = url || parse_urls
+ @url = url || parse_urls
+ @request_domain = request_domain
@known_oembed_paths = {
"rumble.com": {
@@ -23,7 +14,6 @@
format: :json,
},
}
-
return if @url.nil? || @status.preview_cards.any?
@url = @url.to_s
@@ -32,13 +22,13 @@
parsed_uri = Addressable::URI.parse(full_url.to_s)
check_known_short_links(parsed_uri)
- Prometheus::ApplicationExporter::increment(:links, {domain: parsed_uri.normalized_host})
+ Prometheus::ApplicationExporter.increment(:links, { domain: parsed_uri.normalized_host })
end
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
@card = PreviewCard.find_by(url: @url)
- process_url if @card.nil? || @card.updated_at <= 2.weeks.ago || @card.missing_image?
+ process_url if @card.nil? || @card.updated_at <= 2.weeks.ago || @card.missing_image? && !interactive_ad?
else
raise Mastodon::RaceConditionError
end
@@ -48,7 +38,6 @@
attach_card
publish_card_joined_event
end
-
rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
Rails.logger.info "Error fetching link #{@url}: #{e}"
nil
@@ -61,23 +50,19 @@
path = uri.omit(:scheme, :authority, :host).to_s[1..-1]
short_links = {
- "youtu.be": "https://www.youtube.com/watch?v=#{path.sub('?', '&')}"
+ "youtu.be": "https://www.youtube.com/watch?v=#{path.sub('?', '&')}",
}
@url = short_links[domain.to_sym] if short_links[domain.to_sym]
end
def publish_card_joined_event
- Redis.current.publish(
- CardJoinedEvent::EVENT_KEY,
- CardJoinedEvent.new(@card).serialize
- )
+ EventProvider::EventProvider.new('card.joined', CardJoinedEvent, @card).call
end
def process_url
@card ||= PreviewCard.new(url: @url)
-
- attempt_oembed || attempt_opengraph
+ parsed_url.normalized_host == 'rumble.com' ? (attempt_oembed || attempt_opengraph) : (attempt_group || attempt_opengraph || attempt_oembed)
end
def html
@@ -97,7 +82,7 @@
def attach_card
@status.preview_cards << @card
Rails.cache.delete(@status)
- InvalidateSecondaryCacheService.new.call("InvalidateStatusCacheWorker", @status.id)
+ InvalidateSecondaryCacheService.new.call('InvalidateStatusCacheWorker', @status.id)
end
def parse_urls
@@ -108,15 +93,9 @@
links = html.css(':not(.quote-inline) > a')
@all_urls = links.filter_map { |a| Addressable::URI.parse(a['href']) unless skip_link?(a) }.filter_map(&:normalize)
end
-
- @all_urls.reject { |uri| bad_url?(uri) }.first
+ @all_urls.reject { |uri| bad_url_with_group?(uri) }.first
end
- def bad_url?(uri)
- # Avoid local instance URLs and invalid URLs
- uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme)
- end
-
# rubocop:disable Naming/MethodParameterName
def mention_link?(a)
@status.mentions.any? do |mention|
@@ -132,20 +111,25 @@
def attempt_oembed
service = FetchOEmbedService.new
- url_domain = Addressable::URI.parse(@url).normalized_host
+ url_domain = parsed_url.normalized_host
cached_endpoint = Rails.cache.read("oembed_endpoint:#{url_domain}")
embed = service.call(@url, cached_endpoint: cached_endpoint) unless cached_endpoint.nil?
embed ||= service.call(@url, cached_endpoint: @known_oembed_paths[url_domain.to_sym]) if @known_oembed_paths.key?(url_domain.to_sym)
- embed ||= service.call(@url, html: html) unless html.nil?
+ if !embed && !html.nil?
+ service.call(@url, html: html)
+ end
+
return false if embed.nil?
url = Addressable::URI.parse(service.endpoint_url)
+ raise Mastodon::UnexpectedResponseError, service.endpoint_url unless embed[:thumbnail_url].present?
+
@card.type = embed[:type]
@card.title = embed[:title].present? ? CGI.unescapeHTML(embed[:title]) : ''
- @card.author_name = embed[:author_name] || ''
+ @card.author_name = embed[:author_name] || ''
@card.author_url = embed[:author_url].present? ? (url + embed[:author_url]).to_s : ''
@card.provider_name = embed[:provider_name] || ''
@card.provider_url = embed[:provider_url].present? ? (url + embed[:provider_url]).to_s : ''
@@ -202,18 +186,37 @@
@card.title = meta_property(page, 'og:title').presence || page.at_xpath('//title')&.content || ''
@card.description = meta_property(page, 'og:description').presence || meta_property(page, 'description') || ''
- @card.image_remote_url = (Addressable::URI.parse(@url) + meta_property(page, 'og:image')).to_s if meta_property(page, 'og:image')
+ @card.image_remote_url = (parsed_url + meta_property(page, 'og:image')).to_s if meta_property(page, 'og:image')
return if @card.title.blank? && @card.html.blank?
@card.save_with_optional_image!
end
+ def attempt_group
+ return unless group_url?(parsed_url.to_s)
+
+ group_slug = extract_group_slug(parsed_url.to_s)
+ group = Group.find_by!({ slug: group_slug.to_s })
+ @card.title = group.display_name
+ @card.description = group.note
+ @card.image_remote_url = full_asset_url(group.header_static_url) if group.header_file_name
+ @card.save_with_optional_image!
+ end
+
def meta_property(page, property)
page.at_xpath("//meta[contains(concat(' ', normalize-space(@property), ' '), ' #{property} ')]")&.attribute('content')&.value || page.at_xpath("//meta[@name=\"#{property}\"]")&.attribute('content')&.value
end
def lock_options
{ redis: Redis.current, key: "fetch:#{@url}", autorelease: 15.minutes.seconds }
+ end
+
+ def interactive_ad?
+ !!@card&.statuses&.last&.ad
+ end
+
+ def parsed_url
+ Addressable::URI.parse(@url)
end
end
diff -ru truth-old/opensource/app/services/fetch_oembed_service.rb truth-new/opensource/app/services/fetch_oembed_service.rb
--- truth-old/opensource/app/services/fetch_oembed_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/fetch_oembed_service.rb 2024-04-12 09:09:12
@@ -5,6 +5,17 @@
attr_reader :url, :options, :format, :endpoint_url
+ #
+ # Calls the service with a given URL and options.
+ # If the options hash contains a `:cached_endpoint`, it will parse the cached endpoint.
+ # Otherwise, it will discover the endpoint by fetching the HTML of the page and looking
+ # for OEmbed links in the head of the document. After the endpoint is determined, it fetches
+ # the OEmbed data from the endpoint URL.
+ #
+ # @param url [String] the URL to fetch OEmbed data from
+ # @param options [Hash] a hash of options. Can contain a `:cached_endpoint` key with a cached endpoint to use.
+ # @return [Hash, nil] the fetched OEmbed data, or nil if an error occurred
+ #
def call(url, options = {})
@url = url
@options = options
diff -ru truth-old/opensource/app/services/follow_service.rb truth-new/opensource/app/services/follow_service.rb
--- truth-old/opensource/app/services/follow_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/follow_service.rb 2024-04-01 14:59:13
@@ -14,10 +14,11 @@
# @option [Boolean] :bypass_locked
# @option [Boolean] :bypass_limit Allow following past the total follow number
# @option [Boolean] :with_rate_limit
+ # @option [Boolean] :skip_notification Do not notify followed user
def call(source_account, target_account, options = {})
@source_account = source_account
@target_account = target_account
- @options = { bypass_locked: false, bypass_limit: false, with_rate_limit: false }.merge(options)
+ @options = { bypass_locked: false, bypass_limit: false, with_rate_limit: false, skip_notification: false }.merge(options)
raise ActiveRecord::RecordNotFound if following_not_possible?
raise Mastodon::NotPermittedError if following_not_allowed?
@@ -79,12 +80,16 @@
def direct_follow!
follow = @source_account.follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit])
- LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, :follow)
+ LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, :follow) unless @options[:skip_notification]
MergeWorker.perform_async(@target_account.id, @source_account.id)
redis.del("whale:following:#{@source_account.id}") if @target_account.whale?
InvalidateSecondaryCacheService.new.call("InvalidateFollowCacheWorker", @source_account.id, @target_account.id, @target_account.whale?)
+
+ redis.del("avatars_carousel_list_#{@source_account.id}") if @source_account.following_count.to_i < 10
+
+ InvalidateSecondaryCacheService.new.call("InvalidateAvatarsCarouselCacheWorker", @source_account.id)
follow
end
Only in truth-new/opensource/app/services: geo_service.rb
Only in truth-new/opensource/app/services: group_membership_validation_service.rb
Only in truth-new/opensource/app/services: group_search_service.rb
Only in truth-new/opensource/app/services: inspect_link_service.rb
Only in truth-new/opensource/app/services: interactive_ads_service.rb
diff -ru truth-old/opensource/app/services/invalidate_secondary_cache_service.rb truth-new/opensource/app/services/invalidate_secondary_cache_service.rb
--- truth-old/opensource/app/services/invalidate_secondary_cache_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/invalidate_secondary_cache_service.rb 2024-04-01 14:59:13
@@ -5,7 +5,7 @@
return unless secondary_dcs
secondary_dcs.split(',').map(&:strip).each do |dc|
- worker_name.constantize.set(queue: dc).perform_in(1.second, *args)
+ worker_name.constantize.set(queue: dc).perform_async(*args)
end
end
end
Only in truth-new/opensource/app/services: ios_device_check
Only in truth-new/opensource/app/services: join_group_service.rb
Only in truth-new/opensource/app/services: leave_group_service.rb
diff -ru truth-old/opensource/app/services/notify_service.rb truth-new/opensource/app/services/notify_service.rb
--- truth-old/opensource/app/services/notify_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/notify_service.rb 2024-04-01 14:59:13
@@ -15,6 +15,7 @@
@activity = activity
@type = type
@notification = Notification.new(account: @recipient, type: type, activity: @activity)
+
return if recipient.user.nil? || blocked?
@target_status = target_status
@@ -61,6 +62,50 @@
false
end
+ def blocked_chat?
+ false
+ end
+
+ def blocked_chat_message_deleted?
+ false
+ end
+
+ def blocked_group_mention?
+ FeedManager.instance.filter?(:mentions, @notification.mention.status, @recipient)
+ end
+
+ def blocked_group_reblog?
+ false
+ end
+
+ def blocked_group_follow?
+ false
+ end
+
+ def blocked_group_favourite?
+ false
+ end
+
+ def blocked_group_delete?
+ false
+ end
+
+ def blocked_group_approval?
+ false
+ end
+
+ def blocked_group_request?
+ false
+ end
+
+ def blocked_group_promoted?
+ false
+ end
+
+ def blocked_group_demoted?
+ false
+ end
+
def following_sender?
return @following_sender if defined?(@following_sender)
@following_sender = @recipient.following?(@notification.from_account) || @recipient.requested?(@notification.from_account)
@@ -94,6 +139,10 @@
message? && target_status.direct_visibility?
end
+ def chat?
+ @notification.type == :chat
+ end
+
def response_to_recipient?
target_status.in_reply_to_account_id == @recipient.id && target_status.thread&.direct_visibility?
end
@@ -131,6 +180,7 @@
blocked ||= domain_blocking? # Skip for domain blocked accounts
blocked ||= @recipient.blocking?(@notification.from_account) # Skip for blocked accounts
+ blocked ||= @notification.from_account.blocking?(@recipient) # Skip for blocked accounts - the other direction
blocked ||= @recipient.muting_notifications?(@notification.from_account)
blocked ||= hellbanned? # Hellban
blocked ||= optional_non_follower? # Options
Only in truth-new/opensource/app/services: p_tv
diff -ru truth-old/opensource/app/services/post_distribution_service.rb truth-new/opensource/app/services/post_distribution_service.rb
--- truth-old/opensource/app/services/post_distribution_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/post_distribution_service.rb 2024-04-05 09:07:34
@@ -1,24 +1,36 @@
+# frozen_string_literal: true
class PostDistributionService < BaseService
include Redisable
BAILEY_PERCENTAGE = (ENV['BAILEY_PERCENTAGE'] || "0").to_i
- def call(status)
- if rand(1..100) <= BAILEY_PERCENTAGE && !status.account.whale?
- send_to_bailey(status)
+ # Enqueue jobs for both author and followers
+ def distribute_to_author(status)
+ if rand(1..100) <= BAILEY_PERCENTAGE
+ QueueManager.enqueue_status_for_author_distribution(status.id)
+ Rails.logger.debug("bailey_debug: enqueuing status #{status.id}")
else
FanOutOnWriteService.new.call(status)
end
end
- def send_to_bailey(status)
- if status.account.silenced? || !status.public_visibility? || status.reblog?
- rendered = nil
+ def distribute_to_followers(status)
+ if rand(1..100) <= BAILEY_PERCENTAGE
+ QueueManager.enqueue_status_for_follower_distribution(status.id)
+ Rails.logger.debug("bailey_debug: enqueuing status #{status.id}")
else
- rendered = InlineRenderer.render(status, nil, :status)
- rendered = Oj.dump(event: :update, payload: rendered)
+ FanOutOnWriteService.new.call(status)
end
- Redis.current.lpush('elixir:distribution', Oj.dump(job_type: "status_created", status_id: status.id, rendered: rendered))
- Rails.logger.debug("bailey_debug: sending #{rendered.nil? ? 'nil' : 'value'} for rendered for status #{status.id}")
end
+
+ def distribute_to_author_and_followers(status)
+ if rand(1..100) <= BAILEY_PERCENTAGE
+ QueueManager.enqueue_status_for_author_distribution(status.id)
+ QueueManager.enqueue_status_for_follower_distribution(status.id)
+ Rails.logger.debug("bailey_debug: enqueuing status #{status.id}")
+ else
+ FanOutOnWriteService.new.call(status)
+ end
+ end
+
end
diff -ru truth-old/opensource/app/services/post_status_service.rb truth-new/opensource/app/services/post_status_service.rb
--- truth-old/opensource/app/services/post_status_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/post_status_service.rb 2024-04-12 09:09:12
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require "./lib/proto/serializers/status_created_event.rb"
class PostStatusService < BaseService
include Redisable
@@ -14,6 +13,7 @@
# @option [Status] :thread Optional status to reply to
# @option [Boolean] :sensitive
# @option [String] :visibility
+ # @option [Group] :group Optional group to post to, `visibility` must be set to 'group' in that case
# @option [String] :spoiler_text
# @option [String] :language
# @option [String] :scheduled_at
@@ -22,18 +22,26 @@
# @option [Doorkeeper::Application] :application
# @option [String] :idempotency Optional idempotency key
# @option [Boolean] :with_rate_limit
+ # @option [String] :ip_address IP address of where the request originated
# @return [Status]
def call(account, options = {})
- @account = account
- @options = options
- @text = @options[:text] || ''
- @in_reply_to = @options[:thread]
- @quote_id = @options[:quote_id]
- @mentions = @options[:mentions] || []
+ @account = account
+ @options = options
+ @text = @options[:text] || ''
+ @in_reply_to = @options[:thread]
+ @quote_id = @options[:quote_id]
+ @mentions = @options[:mentions] || []
+ @ip_address = @options[:ip_address] || ''
+ @group = @options[:group]
+ @group_timeline_visible = @options[:group_timeline_visible]
+ @group_visibility = @options[:group_visibility]
+ @domain = @options[:domain]
+ @links_service = process_links_service
+
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
- validate_media!
+ @media = validate_media!
preprocess_attributes!
preprocess_quote!
@@ -43,24 +51,21 @@
process_status!
postprocess_status!
bump_potential_friendship!
- create_status_event
+ publish_status_event
export_prometheus_metric
end
redis.setex(idempotency_key, 3_600, @status.id) if idempotency_given?
- send_video_to_upload_worker(@media_ids.first) if video_status?
+ send_video_to_upload_worker
@status
end
private
- def create_status_event
- Redis.current.publish(
- StatusCreatedEvent::EVENT_KEY,
- StatusCreatedEvent.new(@status).serialize
- )
+ def publish_status_event
+ EventProvider::EventProvider.new('status.created', StatusCreatedEvent, @status, @ip_address).call unless @group_visibility == :members_only
end
def status_from_uri(uri)
@@ -90,6 +95,8 @@
@quote_id = quote_from_url(md[1])&.id
@text.sub!(/RT:\s*\[.*?\]/, '')
end
+
+ @text = @links_service.resolve_urls(@text)
rescue ArgumentError
raise ActiveRecord::RecordInvalid
end
@@ -104,13 +111,13 @@
def process_status!
# The following transaction block is needed to wrap the UPDATEs to
# the media attachments when the status is created
-
ApplicationRecord.transaction do
@status = @account.statuses.create!(status_attributes)
+ ProcessMentionsService.new.call(@status, @mentions, @in_reply_to)
end
process_hashtags_service.call(@status)
- process_mentions_service.call(@status, @mentions)
+ @links_service.call(@status)
end
def schedule_status!
@@ -131,45 +138,59 @@
end
def postprocess_status!
- LinkCrawlWorker.perform_async(@status.id) unless @status.spoiler_text? || video_status?
- post_distribution_service.call(@status)
- PollExpirationNotifyWorker.perform_at(@status.poll.expires_at, @status.poll.id) if @status.poll
+ LinkCrawlWorker.perform_async(@status.id, nil, @domain) unless @status.spoiler_text? || video_status?
+ PostDistributionService.new.distribute_to_author(@status)
+ # PollExpirationNotifyWorker.perform_at(@status.poll.expires_at, @status.poll.id) if @status.poll
end
+ #
+ # @return [Array] Returns an empty array if there are no media_ids or if media_ids is not an Enumerable.
+ # Otherwise, it returns the media attachments associated with the account that have not been assigned a status yet.
+ #
+ # @raise [Mastodon::ValidationError] If there are more than 4 media attachments or if a poll is present.
+ # @raise [Mastodon::ValidationError] If any of the media attachments are not processed yet.
+ #
def validate_media!
- return if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable)
+ return [] if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable)
- raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many') if @options[:media_ids].size > 4 || @options[:poll].present?
+ if @options[:media_ids].size > 4 || @options[:poll].present?
+ raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many')
+ end
- @media_ids = @options[:media_ids].take(4).map(&:to_i)
- @media = @account.media_attachments.where(status_id: nil).where(id: @media_ids).sort_by {|m| @media_ids.index(m.id)}
+ media_ids = @options[:media_ids].map(&:to_i)
+ media = @account
+ .media_attachments
+ .where(status_id: nil)
+ .where(id: media_ids)
+ .sort_by { |m| media_ids.index(m.id) }
- raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && @media.find(&:audio_or_video?)
- raise Mastodon::ValidationError, I18n.t('media_attachments.validations.not_ready') if @media.any?(&:not_processed?)
+ if media.any? { |m| !m.video? && m.not_processed? }
+ raise Mastodon::ValidationError, I18n.t('media_attachments.validations.not_ready')
+ end
+
+ media
end
def video_upload_enabled?
ENV['VIDEO_REMOTE_UPLOAD_ENABLED'] == 'true'
end
- def send_video_to_upload_worker(media_attachment_id)
- VideoUploadWorker.perform_async(media_attachment_id, @status.id)
+ def send_video_to_upload_worker
+ @media.each do |m|
+ UploadVideoStatusWorker.perform_async(m.id, @status.id) if m.video?
+ end
end
def language_from_option(str)
ISO_639.find(str)&.alpha2
end
- def process_mentions_service
- ProcessMentionsService.new
- end
-
def process_hashtags_service
ProcessHashtagsService.new
end
- def post_distribution_service
- PostDistributionService.new
+ def process_links_service
+ ProcessStatusLinksService.new
end
def scheduled?
@@ -201,10 +222,13 @@
end
def bump_potential_friendship!
- return if !@status.reply? || @account.id == @status.in_reply_to_account_id
- ActivityTracker.increment('activity:interactions')
- return if @account.following?(@status.in_reply_to_account_id)
- PotentialFriendshipTracker.record(@account.id, @status.in_reply_to_account_id, :reply)
+ if @status.reply? && @account.id != @status.in_reply_to_account_id
+ ActivityTracker.increment('activity:interactions')
+ InteractionsTracker.new(@account.id, @status.in_reply_to_account_id, :reply, @account.following?(@status.in_reply_to_account_id), @status.group).track
+ elsif @status.quote? && @account.id != @status.quote.account_id
+ ActivityTracker.increment('activity:interactions')
+ InteractionsTracker.new(@account.id, @status.quote.account_id, :quote, @account.following?(@status.quote.account_id), @status.quote.group).track
+ end
end
def status_attributes
@@ -212,7 +236,10 @@
text: @text,
media_attachments: @media || [],
thread: @in_reply_to,
- poll_attributes: poll_attributes,
+ group: @group,
+ group_timeline_visible: @group_timeline_visible || false,
+ polls: poll_attributes,
+ has_poll: @options[:poll].present?,
sensitive: @sensitive,
spoiler_text: @options[:spoiler_text] || '',
visibility: @visibility,
@@ -233,13 +260,14 @@
def poll_attributes
return if @options[:poll].blank?
-
- @options[:poll].merge(account: @account, voters_count: 0)
+ @options[:poll][:options_attributes] = @options[:poll].delete(:options).map.with_index { |v, i| { option_number: i, text: v } }
+ [Poll.new(@options[:poll])]
end
def scheduled_options
@options.tap do |options_hash|
options_hash[:in_reply_to_id] = options_hash.delete(:thread)&.id
+ options_hash[:group_id] = options_hash.delete(:group)&.id
options_hash[:application_id] = options_hash.delete(:application)&.id
options_hash[:scheduled_at] = nil
options_hash[:idempotency] = nil
@@ -249,10 +277,10 @@
def export_prometheus_metric
metric_type = @in_reply_to ? :replies : :statuses
- Prometheus::ApplicationExporter::increment(metric_type)
+ Prometheus::ApplicationExporter.increment(metric_type)
end
def video_status?
- @media.present? && @media.first.video? && video_upload_enabled?
+ video_upload_enabled? && @media.any?(&:video?)
end
end
Only in truth-new/opensource/app/services: privatize_media_attachment_service.rb
Only in truth-new/opensource/app/services: process_chat_links_service.rb
diff -ru truth-old/opensource/app/services/process_mentions_service.rb truth-new/opensource/app/services/process_mentions_service.rb
--- truth-old/opensource/app/services/process_mentions_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/process_mentions_service.rb 2024-04-12 09:09:08
@@ -2,50 +2,97 @@
class ProcessMentionsService < BaseService
include Payloadable
+ include Redisable
MAX_MENTIONS = ENV.fetch('MAX_MENTIONS', 15).to_i
+ NOTIFICATIONS_TRESHOLD = 100
+ MENTION_MISMATCH_EXPIRE_AFTER = 7.days.seconds
# Scan status for mentions and fetch remote mentioned users, create
# local mention pointers, send Salmon notifications to mentioned
# remote users
# @param [Status] status
# @param [Enumerable] mentions an array of usernames
- def call(status, mentions)
+ # @param [Status] thread in_reply_to status
+ def call(status, mentions, thread = nil)
+ @passed_mentions = mentions
@status = status
+ @status_mentions = scan_mentions(@status).map(&:downcase)
+ @thread = thread
+ mention_notifications = []
- mentions = mentions.first(MAX_MENTIONS) if mentions.length > MAX_MENTIONS
+ if @status.reply?
+ previous_mentioned_usernames = Account.joins(:mentions).where('mentions.status_id = ?', @thread.id).pluck(:username)
+ @previously_mentioned = (previous_mentioned_usernames << @thread.account.username).map(&:downcase)
+ end
+ group = @status.group_visibility?.presence && @status.group
+ mentions = mentions.first(MAX_MENTIONS) if mentions.length > MAX_MENTIONS
mentioned_accounts = Account.ci_find_by_usernames(mentions)
+ accounts_with_mention_preference = mentioned_accounts.where(receive_only_follow_mentions: true)
+ if accounts_with_mention_preference.any?
+ followers = Follow.where(account: accounts_with_mention_preference, target_account: @status.account).pluck(:account_id)
+ end
+
mentioned_accounts.each do |acc|
next acc if mention_undeliverable?(acc) || acc.suspended?
+ reject_missing_status_mention(acc.username.downcase)
+ # Since mentions are currently tied to audience and notifications, skip mentions
+ # of non-members if private group
+ next acc if (group&.members_only? && !group.members&.where(id: acc.id)&.exists?) || skip_new_account_mentions(acc)
+
+ next acc if acc.receive_only_follow_mentions && !followers.include?(acc.id)
+
new_mention = acc.mentions.new(status: status)
- create_notification(new_mention) if new_mention.save
+ mention_notifications << new_mention if new_mention.save
end
+
+ mention_notifications
end
+ def self.create_notification(status, mention)
+ mentioned_account = mention.account
+ type = status.group ? :group_mention : :mention
+
+ if status.account.followers_count < NOTIFICATIONS_TRESHOLD
+ ProcessMentionNotificationsWorker.perform_in(61.seconds, status.id, mention.id, type.to_sym)
+ else
+ LocalNotificationWorker.perform_async(mentioned_account.id, mention.id, mention.class.name, type.to_sym)
+ end
+ end
+
private
def mention_undeliverable?(mentioned_account)
mentioned_account.nil? || (!mentioned_account.local? && mentioned_account.ostatus?)
end
- def create_notification(mention)
- mentioned_account = mention.account
+ def scan_mentions(status)
+ status.text.scan(Account::MENTION_RE).map(&:second).map(&:downcase)
+ end
- if mentioned_account.local?
- LocalNotificationWorker.perform_async(mentioned_account.id, mention.id, mention.class.name, :mention)
- elsif mentioned_account.activitypub?
- ActivityPub::DeliveryWorker.perform_async(activitypub_json, mention.status.account_id, mentioned_account.inbox_url, { synchronize_followers: !mention.status.distributable? })
+ def reject_missing_status_mention(username)
+ return if @status_mentions.include? username
+
+ if (quote = @status.quote?)
+ return if username == quote.account.username.downcase
+ elsif @status.reply?
+ return if @previously_mentioned.include? username
end
- end
- def activitypub_json
- return @activitypub_json if defined?(@activitypub_json)
- @activitypub_json = Oj.dump(serialize_payload(ActivityPub::ActivityPresenter.from_status(@status), ActivityPub::ActivitySerializer, signer: @status.account))
+ Rails.logger.info "mention_mismatch #{@status.account_id} thread_id: #{@thread&.id} reply_text: #{@status.text} passed_mentions: #{@passed_mentions} previously_mentioned: #{@previously_mentioned}"
+
+ redis_key = "mention_mismatch:#{DateTime.current.to_date}"
+ redis_element_key = @status.account_id
+ redis.zincrby(redis_key, 1, redis_element_key)
+ redis.expire(redis_key, MENTION_MISMATCH_EXPIRE_AFTER)
+
+ raise Mastodon::ValidationError, I18n.t('statuses.errors.mention_mismatch')
end
- def resolve_account_service
- ResolveAccountService.new
+ def skip_new_account_mentions(acc)
+ return false if (Time.now - @status.account.created_at).round > 7.days
+ !@status.reply? || (@status.reply? && !@previously_mentioned.include?(acc.username.downcase))
end
end
Only in truth-new/opensource/app/services: process_status_links_service.rb
Only in truth-new/opensource/app/services: publish_media_attachment_service.rb
diff -ru truth-old/opensource/app/services/reblog_service.rb truth-new/opensource/app/services/reblog_service.rb
--- truth-old/opensource/app/services/reblog_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/reblog_service.rb 2024-04-05 09:07:34
@@ -3,7 +3,10 @@
class ReblogService < BaseService
include Authorization
include Payloadable
+ include Redisable
+ DUPLICATE_REBLOG_EXPIRE_AFTER = 7.days.seconds
+
# Reblog a status and notify its remote author
# @param [Account] account Account to reblog from
# @param [Status] reblogged_status Status to be reblogged
@@ -18,17 +21,27 @@
reblog = account.statuses.find_by(reblog: reblogged_status)
- return reblog unless reblog.nil?
+ unless reblog.nil?
+ if options[:user_agent]
+ redis_key = "duplicate_reblogs:#{DateTime.current.to_date}"
+ redis_element_key = options[:user_agent]
+ redis.zincrby(redis_key, 1, redis_element_key)
+ redis.expire(redis_key, DUPLICATE_REBLOG_EXPIRE_AFTER)
+ end
+ return reblog
+ end
+
visibility = if reblogged_status.hidden?
reblogged_status.visibility
else
options[:visibility] || account.user&.setting_default_privacy
end
- reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility, rate_limit: options[:with_rate_limit])
+ reblog_params = reblogged_status.group_visibility? ? { group_id: reblogged_status.group.id } : {}
+ reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility, rate_limit: options[:with_rate_limit], **reblog_params)
- PostDistributionService.new.call(reblog)
+ PostDistributionService.new.distribute_to_author_and_followers(reblog)
ActivityPub::DistributionWorker.perform_async(reblog.id)
create_notification(reblog)
@@ -43,9 +56,10 @@
def create_notification(reblog)
reblogged_status = reblog.reblog
+ type = reblogged_status.group ? :group_reblog : :reblog
if reblogged_status.account.local?
- LocalNotificationWorker.perform_async(reblogged_status.account_id, reblog.id, reblog.class.name, :reblog)
+ LocalNotificationWorker.perform_async(reblogged_status.account_id, reblog.id, reblog.class.name, type)
elsif reblogged_status.account.activitypub? && !reblogged_status.account.following?(reblog.account)
ActivityPub::DeliveryWorker.perform_async(build_json(reblog), reblog.account_id, reblogged_status.account.inbox_url)
end
@@ -53,10 +67,7 @@
def bump_potential_friendship(account, reblog)
ActivityTracker.increment('activity:interactions')
-
- return if account.following?(reblog.reblog.account_id)
-
- PotentialFriendshipTracker.record(account.id, reblog.reblog.account_id, :reblog)
+ InteractionsTracker.new(account.id, reblog.reblog.account_id, :reblog, account.following?(reblog.reblog.account_id), reblog.group).track
end
def record_use(account, reblog)
Only in truth-new/opensource/app/services: registration_service.rb
Only in truth-new/opensource/app/services: reject_membership_service.rb
diff -ru truth-old/opensource/app/services/remove_status_service.rb truth-new/opensource/app/services/remove_status_service.rb
--- truth-old/opensource/app/services/remove_status_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/remove_status_service.rb 2024-04-01 14:59:13
@@ -4,7 +4,7 @@
include Redisable
include Payloadable
- BAILEY_PERCENTAGE = (ENV['BAILEY_PERCENTAGE'] || "0").to_i
+ BAILEY_PERCENTAGE = (ENV['BAILEY_PERCENTAGE'] || '0').to_i
# Delete a status
# @param [Status] status
@@ -17,13 +17,18 @@
@status = status
@account = status.account
@immediate = options.key?(:immediate) ? options[:immediate] : false
- @options = options
+ @options = options
@status.discard
+ EventProvider::EventProvider.new('status.removed', StatusRemovedEvent, @status, options[:called_by_id]).call if options[:called_by_id] == @status.account_id
+
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
+ remove_user_interactions!
+ remove_from_timeline_cache
+
if rand(1..100) <= BAILEY_PERCENTAGE
send_to_bailey
else
@@ -33,12 +38,7 @@
remove_from_lists
- # There is no reason to send out Undo activities when the
- # cause is that the original object has been removed, since
- # original object being removed implicitly removes reblogs
- # of it. The Delete activity of the original is forwarded
- # separately.
- remove_from_remote_reach if @account.local? && !@options[:original_removed]
+ remove_from_group
unless @status.reblog?
remove_reblogs
@@ -56,6 +56,9 @@
notify_user if options[:notify_user]
end
+ remove_ad_data if @status.ad?
+ purge_cache
+
@status.destroy! if @immediate
else
raise Mastodon::RaceConditionError
@@ -81,7 +84,6 @@
end
end
-
def remove_from_whale_list
FeedManager.instance.remove_from_whale(@status)
end
@@ -102,19 +104,6 @@
end
end
- def remove_from_remote_reach
- # Followers, relays, people who got mentioned in the status,
- # or who reblogged it from someone else might not follow
- # the author and wouldn't normally receive the delete
- # notification - so here, we explicitly send it to them
-
- status_reach_finder = StatusReachFinder.new(@status)
-
- ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes) do |inbox_url|
- [signed_activity_json, @account.id, inbox_url]
- end
- end
-
def signed_activity_json
@signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, signer: @account))
end
@@ -142,6 +131,12 @@
end
end
+ def remove_from_group
+ return unless @status.group_visibility?
+
+ redis.publish("timeline:group:#{@status.group_id}", @payload)
+ end
+
def remove_media
return if @options[:redraft] || !@immediate
@@ -153,8 +148,35 @@
end
def send_to_bailey
- Redis.current.lpush('elixir:distribution', @payload)
- Rails.logger.info("bailey_debug: sending for deletion status #{@status.id}")
+ # Don't create job. Bailey not cleaning up deletes at the moment.
end
+ def remove_user_interactions!
+ if @status.reply? && @account.id != @status.in_reply_to_account_id
+ InteractionsTracker.new(@account.id, @status.in_reply_to_account_id, :reply, @account.following?(@status.in_reply_to_account_id), @status.group).untrack
+ elsif @status.quote? && @account.id != @status.quote.account_id
+ InteractionsTracker.new(@account.id, @status.quote.account_id, :quote, @account.following?(@status.quote.account_id), @status.quote.group).untrack
+ end
+ end
+
+ def remove_from_timeline_cache
+ redis.del("sevro:#{@status.id}")
+ end
+
+ def remove_ad_data
+ @status.preview_cards.each(&:destroy)
+ @status.ad&.destroy
+ end
+end
+
+def purge_cache
+ purge_status(@status)
+ Status.where(in_reply_to_id: @status.id).or(Status.where(quote_id: @status.id)).in_batches.each_record do |reply|
+ purge_status(reply)
+ end
+end
+
+def purge_status(status)
+ Rails.cache.delete(status)
+ InvalidateSecondaryCacheService.new.call('InvalidateStatusCacheWorker', status)
end
diff -ru truth-old/opensource/app/services/report_service.rb truth-new/opensource/app/services/report_service.rb
--- truth-old/opensource/app/services/report_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/report_service.rb 2024-04-01 14:59:13
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require "./lib/proto/serializers/report_created_event.rb"
class ReportService < BaseService
include Payloadable
@@ -8,12 +7,13 @@
@source_account = source_account
@target_account = target_account
@status_ids = options.delete(:status_ids) || []
+ @message_ids = options.delete(:message_ids) || []
@rule_ids = options.delete(:rule_ids) || []
@comment = options.delete(:comment) || ''
+ @group_id = options.delete(:group_id) || nil
+ @external_ad_id = options.delete(:external_ad_id) || nil
@options = options
- raise ActiveRecord::RecordNotFound if @target_account.suspended?
-
create_report!
publish_event!
#notify_staff!
@@ -28,10 +28,14 @@
@report = @source_account.reports.create!(
target_account: @target_account,
status_ids: @status_ids,
+ message_ids: @message_ids,
rule_ids: @rule_ids,
comment: @comment,
uri: @options[:uri],
- forwarded: ActiveModel::Type::Boolean.new.cast(@options[:forward])
+ forwarded: ActiveModel::Type::Boolean.new.cast(@options[:forward]),
+ rate_limit: true,
+ group_id: @group_id,
+ external_ad_id: @external_ad_id
)
end
@@ -42,24 +46,45 @@
@report.status_ids << nil
end
- @report.status_ids.each do |status_id|
- attachments = MediaAttachment.where(status_id: status_id)
+ if @report.message_ids.empty?
+ @report.message_ids << nil
+ end
+ if @group_id && @report.status_ids[0].nil?
+ # reporting a group
report_data = OpenStruct.new(
@report.attributes.merge(
- status_id: status_id,
- status_ids: @report.status_ids.compact,
report_set_id: set_uuid,
- account_username: @report.account.username,
- target_account_username: @report.target_account.username,
- image_ids: attachments.select { |a| a.image? }.pluck(:id),
- video_ids: attachments.select { |a| a.video? }.pluck(:id)
+ display_name: @report.account.username,
+ owner_id: @target_account.id,
+ group_id: @group_id
)
)
- Redis.current.publish(
- ReportCreatedEvent::EVENT_KEY,
- ReportCreatedEvent.new(report_data).serialize
- )
+ EventProvider::EventProvider.new("group_report.created", GroupReportCreatedEvent, report_data).call
+ elsif @report.message_ids[0].nil?
+ # reporting a status
+ @report.status_ids.each do |status_id|
+ attachments = status_id ? MediaAttachment.where(status_id: status_id) : []
+
+ report_data = OpenStruct.new(
+ @report.attributes.merge(
+ status_id: status_id,
+ report_set_id: set_uuid,
+ image_ids: attachments.select { |a| a.image? }.pluck(:id),
+ video_ids: attachments.select { |a| a.video? }.pluck(:id),
+ group_id: @group_id
+ )
+ )
+ EventProvider::EventProvider.new("report.created", ReportCreatedEvent, report_data).call
+ end
+ elsif @external_ad_id.nil?
+ # reporting a message
+ @report.message_ids.each do |message_id|
+ report_data = OpenStruct.new(
+ @report.attributes.merge(message_id: message_id)
+ )
+ EventProvider::EventProvider.new("chat_report.created", ChatMessageReportCreatedEvent, report_data).call
+ end
end
end
diff -ru truth-old/opensource/app/services/resolve_account_service.rb truth-new/opensource/app/services/resolve_account_service.rb
--- truth-old/opensource/app/services/resolve_account_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/resolve_account_service.rb 2024-04-01 14:59:13
@@ -140,7 +140,7 @@
end
def queue_deletion!
- AccountDeletionWorker.perform_async(@account.id, reserve_username: false, skip_activitypub: true)
+ AccountDeletionWorker.perform_async(@account.id, -99, reserve_username: false, skip_activitypub: true)
end
def lock_options
Only in truth-new/opensource/app/services: revoke_membership_service.rb
Only in truth-new/opensource/app/services: rumble
diff -ru truth-old/opensource/app/services/search_service.rb truth-new/opensource/app/services/search_service.rb
--- truth-old/opensource/app/services/search_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/search_service.rb 2024-04-12 09:09:12
@@ -7,9 +7,11 @@
PROHIBITED_TERMS = ENV.fetch('PROHIBITED_TERMS', '').split(',').freeze
PROHIBITED_FILTERS = { bool: { must_not: PROHIBITED_TERMS.map { |term| { multi_match: { type: 'most_fields', query: term } } } } }.freeze
+ # https://github.com/franklsf95/ruby-emoji-regex
+ EMOJI_REGEX = /[\u{00A9}\u{00AE}\u{203C}\u{2049}\u{2122}\u{2139}\u{2194}-\u{2199}\u{21A9}-\u{21AA}\u{231A}-\u{231B}\u{2328}\u{23CF}\u{23E9}-\u{23F3}\u{23F8}-\u{23FA}\u{24C2}\u{25AA}-\u{25AB}\u{25B6}\u{25C0}\u{25FB}-\u{25FE}\u{2600}-\u{2604}\u{260E}\u{2611}\u{2614}-\u{2615}\u{2618}\u{261D}\u{2620}\u{2622}-\u{2623}\u{2626}\u{262A}\u{262E}-\u{262F}\u{2638}-\u{263A}\u{2640}\u{2642}\u{2648}-\u{2653}\u{2660}\u{2663}\u{2665}-\u{2666}\u{2668}\u{267B}\u{267F}\u{2692}-\u{2697}\u{2699}\u{269B}-\u{269C}\u{26A0}-\u{26A1}\u{26AA}-\u{26AB}\u{26B0}-\u{26B1}\u{26BD}-\u{26BE}\u{26C4}-\u{26C5}\u{26C8}\u{26CE}-\u{26CF}\u{26D1}\u{26D3}-\u{26D4}\u{26E9}-\u{26EA}\u{26F0}-\u{26F5}\u{26F7}-\u{26FA}\u{26FD}\u{2702}\u{2705}\u{2708}-\u{270D}\u{270F}\u{2712}\u{2714}\u{2716}\u{271D}\u{2721}\u{2728}\u{2733}-\u{2734}\u{2744}\u{2747}\u{274C}\u{274E}\u{2753}-\u{2755}\u{2757}\u{2763}-\u{2764}\u{2795}-\u{2797}\u{27A1}\u{27B0}\u{27BF}\u{2934}-\u{2935}\u{2B05}-\u{2B07}\u{2B1B}-\u{2B1C}\u{2B50}\u{2B55}\u{3030}\u{303D}\u{3297}\u{3299}\u{1F004}\u{1F0CF}\u{1F170}-\u{1F171}\u{1F17E}-\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}-\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}-\u{1F251}\u{1F300}-\u{1F321}\u{1F324}-\u{1F393}\u{1F396}-\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}-\u{1F3F0}\u{1F3F3}-\u{1F3F5}\u{1F3F7}-\u{1F4FD}\u{1F4FF}-\u{1F53D}\u{1F549}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F56F}-\u{1F570}\u{1F573}-\u{1F57A}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F590}\u{1F595}-\u{1F596}\u{1F5A4}-\u{1F5A5}\u{1F5A8}\u{1F5B1}-\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5E8}\u{1F5EF}\u{1F5F3}\u{1F5FA}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CB}-\u{1F6D2}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6EB}-\u{1F6EC}\u{1F6F0}\u{1F6F3}-\u{1F6F8}\u{1F910}-\u{1F93A}\u{1F93C}-\u{1F93E}\u{1F940}-\u{1F945}\u{1F947}-\u{1F94C}\u{1F950}-\u{1F96B}\u{1F980}-\u{1F997}\u{1F9C0}\u{1F9D0}-\u{1F9E6}\u{200D}\u{20E3}\u{FE0F}\u{E0020}-\u{E007F}\u{2388}\u{2605}\u{2607}-\u{260D}\u{260F}-\u{2610}\u{2612}\u{2616}-\u{2617}\u{2619}-\u{261C}\u{261E}-\u{261F}\u{2621}\u{2624}-\u{2625}\u{2627}-\u{2629}\u{262B}-\u{262D}\u{2630}-\u{2637}\u{263B}-\u{263F}\u{2641}\u{2643}-\u{2647}\u{2654}-\u{265F}\u{2661}-\u{2662}\u{2664}\u{2667}\u{2669}-\u{267A}\u{267C}-\u{267E}\u{2680}-\u{2691}\u{2698}\u{269A}\u{269D}-\u{269F}\u{26A2}-\u{26A9}\u{26AC}-\u{26AF}\u{26B2}-\u{26BC}\u{26BF}-\u{26C3}\u{26C6}-\u{26C7}\u{26C9}-\u{26CD}\u{26D0}\u{26D2}\u{26D5}-\u{26E8}\u{26EB}-\u{26EF}\u{26F6}\u{26FB}-\u{26FC}\u{26FE}-\u{2701}\u{2703}-\u{2704}\u{270E}\u{2710}-\u{2711}\u{2765}-\u{2767}\u{1F000}-\u{1F003}\u{1F005}-\u{1F0CE}\u{1F0D0}-\u{1F0FF}\u{1F10D}-\u{1F10F}\u{1F12F}\u{1F16C}-\u{1F16F}\u{1F1AD}-\u{1F1E5}\u{1F203}-\u{1F20F}\u{1F23C}-\u{1F23F}\u{1F249}-\u{1F24F}\u{1F252}-\u{1F2FF}\u{1F322}-\u{1F323}\u{1F394}-\u{1F395}\u{1F398}\u{1F39C}-\u{1F39D}\u{1F3F1}-\u{1F3F2}\u{1F3F6}\u{1F4FE}\u{1F53E}-\u{1F548}\u{1F54F}\u{1F568}-\u{1F56E}\u{1F571}-\u{1F572}\u{1F57B}-\u{1F586}\u{1F588}-\u{1F589}\u{1F58E}-\u{1F58F}\u{1F591}-\u{1F594}\u{1F597}-\u{1F5A3}\u{1F5A6}-\u{1F5A7}\u{1F5A9}-\u{1F5B0}\u{1F5B3}-\u{1F5BB}\u{1F5BD}-\u{1F5C1}\u{1F5C5}-\u{1F5D0}\u{1F5D4}-\u{1F5DB}\u{1F5DF}-\u{1F5E0}\u{1F5E2}\u{1F5E4}-\u{1F5E7}\u{1F5E9}-\u{1F5EE}\u{1F5F0}-\u{1F5F2}\u{1F5F4}-\u{1F5F9}\u{1F6C6}-\u{1F6CA}\u{1F6D3}-\u{1F6DF}\u{1F6E6}-\u{1F6E8}\u{1F6EA}\u{1F6ED}-\u{1F6EF}\u{1F6F1}-\u{1F6F2}\u{1F6F9}-\u{1F6FF}\u{1F774}-\u{1F77F}\u{1F7D5}-\u{1F7FF}\u{1F80C}-\u{1F80F}\u{1F848}-\u{1F84F}\u{1F85A}-\u{1F85F}\u{1F888}-\u{1F88F}\u{1F8AE}-\u{1F90F}\u{1F93F}\u{1F94D}-\u{1F94F}\u{1F96C}-\u{1F97F}\u{1F998}-\u{1F9BF}\u{1F9C1}-\u{1F9CF}]/.freeze
def call(query, account, limit, options = {})
- @query = query&.strip
+ @query = create_query(query)
@account = account
@options = options
@limit = limit.to_i
@@ -25,10 +27,15 @@
results[:accounts] = perform_accounts_search! if account_searchable?
results[:statuses] = perform_statuses_search! if full_text_searchable?
results[:hashtags] = perform_hashtags_search! if hashtag_searchable?
+ results[:groups] = perform_groups_search! if group_searchable?
end
end
end
+ def create_query(q)
+ q&.gsub(EMOJI_REGEX, '')&.strip
+ end
+
private
def perform_accounts_search!
@@ -37,11 +44,19 @@
@account,
limit: @limit,
resolve: @resolve,
- offset: @offset
+ offset: @offset,
)
end
add_method_tracer :perform_accounts_search!, 'SearchService/perform_accounts_search!'
+ def perform_groups_search!
+ GroupSearchService.new(
+ @query,
+ limit: @limit,
+ offset: @offset,
+ ).call
+ end
+
def perform_statuses_search!
functions = [activity_score_function, time_distance_function]
@@ -55,13 +70,13 @@
end
results = StatusesIndex
- .query(function_score: function_score_query)
- .filter(range: { id: id_range })
- .filter(PROHIBITED_FILTERS)
- .limit(@limit)
- .offset(@offset)
- .objects
- .compact
+ .query(function_score: function_score_query)
+ .filter(range: { id: id_range })
+ .filter(PROHIBITED_FILTERS)
+ .limit(@limit)
+ .offset(@offset)
+ .objects
+ .compact
account_ids = results.map(&:account_id)
account_domains = results.map(&:account_domain)
@@ -97,17 +112,14 @@
end
def perform_hashtags_search!
- TagSearchService.new.call(
- @query,
- limit: @limit,
- offset: @offset,
- exclude_unreviewed: @options[:exclude_unreviewed]
- )
+ result = Tag.search_for(@query, @limit, @offset)
+ JSON.parse(result || '[]')
end
+
add_method_tracer :perform_hashtags_search!, 'SearchService/perform_hashtags_search!'
def default_results
- { accounts: [], hashtags: [], statuses: [] }
+ { accounts: [], hashtags: [], statuses: [], groups: [] }
end
def url_query?
@@ -136,6 +148,10 @@
account_search? && !(@query.start_with?('#') || (@query.include?('@') && @query.include?(' ')))
end
+ def group_searchable?
+ group_search? && !(@query.start_with?('#') || (@query.include?('@') && @query.include?(' ')))
+ end
+
def hashtag_searchable?
hashtag_search? && !@query.include?('@')
end
@@ -150,6 +166,10 @@
def statuses_search?
@options[:type].blank? || @options[:type] == 'statuses'
+ end
+
+ def group_search?
+ @options[:type].blank? || @options[:type] == 'groups'
end
def relations_map_for_account(account, account_ids, domains)
Only in truth-new/opensource/app/services: sk_ad_network_service.rb
diff -ru truth-old/opensource/app/services/suspend_account_service.rb truth-new/opensource/app/services/suspend_account_service.rb
--- truth-old/opensource/app/services/suspend_account_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/suspend_account_service.rb 2024-04-01 14:59:13
@@ -8,10 +8,9 @@
suspend!
reject_remote_follows!
- distribute_update_actor!
unmerge_from_home_timelines!
unmerge_from_list_timelines!
- privatize_media_attachments!
+ Admin::MediaPrivatizationWorker.perform_async(account.id)
end
private
@@ -60,40 +59,6 @@
def unmerge_from_list_timelines!
@account.lists_for_local_distribution.find_each do |list|
FeedManager.instance.unmerge_from_list(@account, list)
- end
- end
-
- def privatize_media_attachments!
- attachment_names = MediaAttachment.attachment_definitions.keys
-
- @account.media_attachments.find_each do |media_attachment|
- attachment_names.each do |attachment_name|
- attachment = media_attachment.public_send(attachment_name)
- styles = [:original] | attachment.styles.keys
-
- next if attachment.blank?
-
- styles.each do |style|
- case Paperclip::Attachment.default_options[:storage]
- when :s3
- begin
- attachment.s3_object(style).acl.put(acl: 'private')
- rescue Aws::S3::Errors::NoSuchKey
- Rails.logger.warn "Tried to change acl on non-existent key #{attachment.s3_object(style).key}"
- end
- when :fog
- # Not supported
- when :filesystem
- begin
- FileUtils.chmod(0o600 & ~File.umask, attachment.path(style)) unless attachment.path(style).nil?
- rescue Errno::ENOENT
- Rails.logger.warn "Tried to change permission on non-existent file #{attachment.path(style)}"
- end
- end
-
- CacheBusterWorker.perform_async(attachment.path(style)) if Rails.configuration.x.cache_buster_enabled
- end
- end
end
end
Only in truth-old/opensource/app/services: tag_search_service.rb
diff -ru truth-old/opensource/app/services/unblock_service.rb truth-new/opensource/app/services/unblock_service.rb
--- truth-old/opensource/app/services/unblock_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/unblock_service.rb 2024-04-01 14:59:13
@@ -7,6 +7,7 @@
return unless account.blocking?(target_account)
unblock = account.unblock!(target_account)
+ InvalidateSecondaryCacheService.new.call("InvalidateFollowCacheWorker", account.id, target_account.id, target_account.whale?)
create_notification(unblock) if !target_account.local? && target_account.activitypub?
unblock
end
diff -ru truth-old/opensource/app/services/unfavourite_service.rb truth-new/opensource/app/services/unfavourite_service.rb
--- truth-old/opensource/app/services/unfavourite_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/unfavourite_service.rb 2024-04-01 14:59:13
@@ -7,6 +7,7 @@
favourite = Favourite.find_by!(account: account, status: status)
favourite.destroy!
create_notification(favourite) if !status.account.local? && status.account.activitypub?
+ InteractionsTracker.new(account.id, status.account_id, :favourite, account.following?(status.account_id), status.group).untrack
favourite
end
diff -ru truth-old/opensource/app/services/unfollow_service.rb truth-new/opensource/app/services/unfollow_service.rb
--- truth-old/opensource/app/services/unfollow_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/unfollow_service.rb 2024-04-01 14:59:13
@@ -34,6 +34,8 @@
InvalidateSecondaryCacheService.new.call("InvalidateFollowCacheWorker", @source_account.id, @target_account.id, @target_account.whale?)
+ remove_follower_interactions(@source_account.id, @target_account.id)
+
follow
end
@@ -63,5 +65,11 @@
def build_reject_json(follow)
Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer))
+ end
+
+ def remove_follower_interactions(source_account_id, target_account_id)
+ InteractionsTracker.new(source_account_id, target_account_id, false, true).remove
+ redis.del("avatars_carousel_list_#{source_account_id}")
+ InvalidateSecondaryCacheService.new.call("InvalidateAvatarsCarouselCacheWorker", source_account_id)
end
end
diff -ru truth-old/opensource/app/services/unsuspend_account_service.rb truth-new/opensource/app/services/unsuspend_account_service.rb
--- truth-old/opensource/app/services/unsuspend_account_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/unsuspend_account_service.rb 2024-04-01 14:59:13
@@ -11,8 +11,7 @@
merge_into_home_timelines!
merge_into_list_timelines!
- publish_media_attachments!
- distribute_update_actor!
+ Admin::MediaPublicationWorker.perform_async(account.id)
end
private
@@ -56,40 +55,6 @@
def merge_into_list_timelines!
@account.lists_for_local_distribution.find_each do |list|
FeedManager.instance.merge_into_list(@account, list)
- end
- end
-
- def publish_media_attachments!
- attachment_names = MediaAttachment.attachment_definitions.keys
-
- @account.media_attachments.find_each do |media_attachment|
- attachment_names.each do |attachment_name|
- attachment = media_attachment.public_send(attachment_name)
- styles = [:original] | attachment.styles.keys
-
- next if attachment.blank?
-
- styles.each do |style|
- case Paperclip::Attachment.default_options[:storage]
- when :s3
- begin
- attachment.s3_object(style).acl.put(acl: Paperclip::Attachment.default_options[:s3_permissions])
- rescue Aws::S3::Errors::NoSuchKey
- Rails.logger.warn "Tried to change acl on non-existent key #{attachment.s3_object(style).key}"
- end
- when :fog
- # Not supported
- when :filesystem
- begin
- FileUtils.chmod(0o666 & ~File.umask, attachment.path(style)) unless attachment.path(style).nil?
- rescue Errno::ENOENT
- Rails.logger.warn "Tried to change permission on non-existent file #{attachment.path(style)}"
- end
- end
-
- CacheBusterWorker.perform_async(attachment.path(style)) if Rails.configuration.x.cache_buster_enabled
- end
- end
end
end
diff -ru truth-old/opensource/app/services/update_account_service.rb truth-new/opensource/app/services/update_account_service.rb
--- truth-old/opensource/app/services/update_account_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/update_account_service.rb 2024-04-01 14:59:13
@@ -1,6 +1,4 @@
# frozen_string_literal: true
-require "./lib/proto/serializers/account_updated_event.rb"
-
class UpdateAccountService < BaseService
def call(account, params, raise_error: false)
was_locked = account.locked
@@ -8,6 +6,7 @@
params['settings_store'] = params['pleroma_settings_store']
params.delete('pleroma_settings_store')
+ params.delete('chats_onboarded')
if !params['settings_store']
params.delete('settings_store')
@@ -16,12 +15,15 @@
account.send(update_method, params).tap do |ret|
next unless ret
+ # Allow resetting header images by passing in empty string
+ if params[:header] && params[:header].to_s.empty?
+ account.header_file_name = nil
+ account.header_content_type = nil
+ account.header_file_size = nil
+ end
+
authorize_all_follow_requests(account) if was_locked && !account.locked
check_links(account)
- Redis.current.publish(
- AccountUpdatedEvent::EVENT_KEY,
- AccountUpdatedEvent.new(account, fields_changed(account)).serialize
- )
process_hashtags(account)
end
rescue Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
@@ -45,14 +47,5 @@
def process_hashtags(account)
account.tags_as_strings = Extractor.extract_hashtags(account.note)
- end
-
- def fields_changed(account)
- updatable_fields = %w(display_name avatar_url header_url website bio location)
- changed_fields = account.saved_changes.keys
- updated_fields = changed_fields.select { |f| updatable_fields.include?(f) }
- updated_fields << "avatar_url" if changed_fields.include?("avatar_file_name")
- updated_fields << "header_url" if changed_fields.include?("header_file_name")
- updated_fields.map(&:upcase)
end
end
Only in truth-new/opensource/app/services: update_feed_service.rb
Only in truth-new/opensource/app/services: update_group_service.rb
Only in truth-new/opensource/app/services: upload_video_chat_service.rb
Only in truth-old/opensource/app/services: upload_video_service.rb
Only in truth-new/opensource/app/services: upload_video_status_service.rb
Only in truth-new/opensource/app/services: video_encoding_status_service.rb
Only in truth-new/opensource/app/services: video_preview_service.rb
diff -ru truth-old/opensource/app/services/vote_service.rb truth-new/opensource/app/services/vote_service.rb
--- truth-old/opensource/app/services/vote_service.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/services/vote_service.rb 2024-04-12 09:09:08
@@ -16,11 +16,11 @@
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
- already_voted = @poll.votes.where(account: @account).exists?
+ already_voted = @poll.voted?(@account)
ApplicationRecord.transaction do
@choices.each do |choice|
- @votes << @poll.votes.create!(account: @account, choice: Integer(choice))
+ @votes << @poll.votes.create!(account: @account, option_number: Integer(choice))
end
end
else
@@ -28,52 +28,18 @@
end
end
- increment_voters_count! unless already_voted
-
ActivityTracker.increment('activity:interactions')
-
- if @poll.account.local?
- distribute_poll!
- else
- deliver_votes!
- queue_final_poll_check!
- end
end
private
- def distribute_poll!
- return if @poll.hide_totals?
- ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, @poll.status.id)
- end
-
def queue_final_poll_check!
return unless @poll.expires?
- PollExpirationNotifyWorker.perform_at(@poll.expires_at + 5.minutes, @poll.id)
+ #PollExpirationNotifyWorker.perform_at(@poll.expires_at + 5.minutes, @poll.id)
end
- def deliver_votes!
- @votes.each do |vote|
- ActivityPub::DeliveryWorker.perform_async(
- build_json(vote),
- @account.id,
- @poll.account.inbox_url
- )
- end
- end
-
def build_json(vote)
Oj.dump(serialize_payload(vote, ActivityPub::VoteSerializer))
- end
-
- def increment_voters_count!
- unless @poll.voters_count.nil?
- @poll.voters_count = @poll.voters_count + 1
- @poll.save
- end
- rescue ActiveRecord::StaleObjectError
- @poll.reload
- retry
end
def lock_options
Only in truth-new/opensource/app/validators: account_feed_validator.rb
Only in truth-new/opensource/app/validators: base_email_validator.rb
Only in truth-new/opensource/app/validators: feed_validator.rb
diff -ru truth-old/opensource/app/validators/follow_limit_validator.rb truth-new/opensource/app/validators/follow_limit_validator.rb
--- truth-old/opensource/app/validators/follow_limit_validator.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/validators/follow_limit_validator.rb 2024-04-01 14:59:13
@@ -9,7 +9,7 @@
if limit_reached?(follow.account)
follow.errors.add(:base, I18n.t('users.follow_limit_reached', limit: self.class.limit_for_account(follow.account)))
- follow.errors.add(:errorCode, 'followLimitReached')
+ follow.errors.add(:error_code, 'followLimitReached')
end
end
Only in truth-new/opensource/app/validators: group_status_pin_validator.rb
Only in truth-new/opensource/app/validators: max_group_admin_validator.rb
Only in truth-new/opensource/app/validators: max_group_tag_validator.rb
diff -ru truth-old/opensource/app/validators/note_length_validator.rb truth-new/opensource/app/validators/note_length_validator.rb
--- truth-old/opensource/app/validators/note_length_validator.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/validators/note_length_validator.rb 2024-04-01 14:59:13
@@ -14,9 +14,8 @@
def countable_text(value)
return '' if value.nil?
- value.dup.tap do |new_text|
- new_text.gsub!(FetchLinkCardService::URL_PATTERN, StatusLengthValidator::URL_PLACEHOLDER)
- new_text.gsub!(Account::MENTION_RE, '@\2')
- end
+ value
+ .gsub(FetchLinkCardService::URL_PATTERN) { |url| URLPlaceholder.generate(url) }
+ .gsub(Account::MENTION_RE, '@\2')
end
end
diff -ru truth-old/opensource/app/validators/poll_validator.rb truth-new/opensource/app/validators/poll_validator.rb
--- truth-old/opensource/app/validators/poll_validator.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/validators/poll_validator.rb 2024-04-01 14:59:13
@@ -1,9 +1,9 @@
# frozen_string_literal: true
class PollValidator < ActiveModel::Validator
- MAX_OPTIONS = 4
+ MAX_OPTIONS = 6
MAX_OPTION_CHARS = 50
- MAX_EXPIRATION = 1.month.freeze
+ MAX_EXPIRATION = 7.days.freeze
MIN_EXPIRATION = 5.minutes.freeze
def validate(poll)
@@ -11,8 +11,7 @@
poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size > 1
poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS
- poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS }
- poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size
+ poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq(&:text).size == poll.options.map(&:text).size
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_long')) if poll.expires_at.nil? || poll.expires_at - current_time > MAX_EXPIRATION
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_short')) if poll.expires_at.present? && (poll.expires_at - current_time).ceil < MIN_EXPIRATION
end
Only in truth-new/opensource/app/validators: status_group_validator.rb
diff -ru truth-old/opensource/app/validators/status_length_validator.rb truth-new/opensource/app/validators/status_length_validator.rb
--- truth-old/opensource/app/validators/status_length_validator.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/validators/status_length_validator.rb 2024-04-01 14:59:13
@@ -1,9 +1,7 @@
# frozen_string_literal: true
class StatusLengthValidator < ActiveModel::Validator
- MAX_CHARS = 500
- URL_PLACEHOLDER_CHARS = 23
- URL_PLACEHOLDER = "\1#{'x' * URL_PLACEHOLDER_CHARS}"
+ MAX_CHARS = 1000
def validate(status)
return unless status.local? && !status.reblog?
@@ -29,9 +27,8 @@
def countable_text
return '' if @status.text.nil?
- @status.text.dup.tap do |new_text|
- new_text.gsub!(FetchLinkCardService::URL_PATTERN, URL_PLACEHOLDER)
- new_text.gsub!(Account::MENTION_RE, '@\2')
- end
+ @status.text
+ .gsub(FetchLinkCardService::URL_PATTERN) { |url| URLPlaceholder.generate(url) }
+ .gsub(Account::MENTION_RE, '@\2')
end
end
diff -ru truth-old/opensource/app/validators/status_pin_validator.rb truth-new/opensource/app/validators/status_pin_validator.rb
--- truth-old/opensource/app/validators/status_pin_validator.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/validators/status_pin_validator.rb 2024-04-01 14:59:13
@@ -5,6 +5,8 @@
pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
- pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4 && pin.account.local?
+ pin.errors.add(:base, I18n.t('statuses.pin_errors.direct')) if pin.status.direct_visibility?
+ pin.errors.add(:base, I18n.t('statuses.pin_errors.group')) if pin.status.group_visibility?
+ pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.profile_pins.size > 4 && pin.account.local?
end
end
Only in truth-new/opensource/app/validators: unique_password_validator.rb
Only in truth-new/opensource/app/validators: valid_group_name_validator.rb
diff -ru truth-old/opensource/app/validators/vote_validator.rb truth-new/opensource/app/validators/vote_validator.rb
--- truth-old/opensource/app/validators/vote_validator.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/validators/vote_validator.rb 2024-04-01 14:59:13
@@ -6,7 +6,7 @@
vote.errors.add(:base, I18n.t('polls.errors.invalid_choice')) if invalid_choice?(vote)
- if vote.poll.multiple? && vote.poll.votes.where(account: vote.account, choice: vote.choice).exists?
+ if vote.poll.multiple? && vote.poll.votes.where(account: vote.account, option_number: vote.option_number).exists?
vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
elsif !vote.poll.multiple? && vote.poll.votes.where(account: vote.account).exists?
vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
@@ -16,6 +16,6 @@
private
def invalid_choice?(vote)
- vote.choice.negative? || vote.choice >= vote.poll.options.size
+ vote.option_number.negative? || vote.option_number >= vote.poll.options.size
end
end
diff -ru truth-old/opensource/app/views/admin/reports/_status.html.haml truth-new/opensource/app/views/admin/reports/_status.html.haml
--- truth-old/opensource/app/views/admin/reports/_status.html.haml 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/admin/reports/_status.html.haml 2024-04-01 14:59:13
@@ -1,3 +1,5 @@
+- show_author ||= false
+
.batch-table__row
%label.batch-table__row__select.batch-checkbox
= f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
@@ -33,7 +35,14 @@
= t('statuses.boosted_from_html', acct_link: admin_account_inline_link_to(status.proper.account))
- else
= fa_visibility_icon(status)
- = t("statuses.visibilities.#{status.visibility}")
+ - if status.group_visibility?
+ - if status.group.present?
+ = link_to admin_group_path(status.group.id), class: 'detailed-status__group' do
+ = t("statuses.visibilities.#{status.visibility}")
+ - else
+ = t("statuses.visibilities.#{status.visibility}")
+ - else
+ = t("statuses.visibilities.#{status.visibility}")
- if status.proper.sensitive?
ยท
= fa_icon('eye-slash fw')
Only in truth-old/opensource/app/views/admin: trending_truths
Only in truth-new/opensource/app/views: api
Only in truth-new/opensource/app/views/application: _group_card.html.haml
diff -ru truth-old/opensource/app/views/layouts/application.html.haml truth-new/opensource/app/views/layouts/application.html.haml
--- truth-old/opensource/app/views/layouts/application.html.haml 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/layouts/application.html.haml 2024-04-01 14:59:13
@@ -21,7 +21,6 @@
%link{ rel: 'apple-touch-icon', sizes: '180x180', href: '/icons/icon-180x180.png' }/
%link{ rel: 'apple-touch-icon', sizes: '192x192', href: '/icons/icon-192x192.png' }/
%link{ rel: 'apple-touch-icon', sizes: '512x512', href: '/icons/icon-512x512.png' }/
- %link{ rel: 'mask-icon', href: '/mask-icon.svg', color: '#2B90D9' }/
%link{ rel: 'manifest', href: '/manifest.json' }/
%meta{ name: 'msapplication-config', content: '/browserconfig.xml' }/
%meta{ name: 'theme-color', content: '#282c37' }/
Only in truth-new/opensource/app/views/layouts: docs.html.haml
Only in truth-new/opensource/app/views: link
diff -ru truth-old/opensource/app/views/notification_mailer/user_approved.html.erb truth-new/opensource/app/views/notification_mailer/user_approved.html.erb
--- truth-old/opensource/app/views/notification_mailer/user_approved.html.erb 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/notification_mailer/user_approved.html.erb 2024-04-01 14:59:13
@@ -40,7 +40,24 @@
</p>
<p class="text-gray-600 mb-0">
- <%= t 'notification_mailer.user_approved.extra_step' %>
+ <%= t(
+ 'notification_mailer.user_approved.extra_step',
+ help_center_link: link_to(
+ t('notification_mailer.user_approved.help_center'),
+ 'https://help.truthsocial.com',
+ class: 'text-primary-600',
+ target: '_blank'
+ ),
+ mailto_support: link_to(
+ 'support@truthsocial.com',
+ 'mailto:support@truthsocial.com',
+ class: 'text-primary-600',
+ )
+ ).html_safe %>
+ </p>
+
+ <p class="text-gray-600 mb-0">
+ <%= t("notification_mailer.user_approved.signoff") %>
</p>
<table class="w-full" role="separator" cellpadding="0" cellspacing="0">
diff -ru truth-old/opensource/app/views/notification_mailer/user_approved.text.erb truth-new/opensource/app/views/notification_mailer/user_approved.text.erb
--- truth-old/opensource/app/views/notification_mailer/user_approved.text.erb 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/notification_mailer/user_approved.text.erb 2024-04-01 14:59:13
@@ -7,7 +7,15 @@
<%= t 'notification_mailer.user_approved.explanation' %>
-<%= t 'notification_mailer.user_approved.extra_step' %>
+<%= t(
+ 'notification_mailer.user_approved.extra_step',
+ help_center_link: t('notification_mailer.user_approved.help_center'),
+ mailto_support: 'support@truthsocial.com'
+) %>
+
+=> https://help.truthsocial.com
+
+<%= t("notification_mailer.user_approved.signoff") %>
---
diff -ru truth-old/opensource/app/views/statuses/_simple_status.html.haml truth-new/opensource/app/views/statuses/_simple_status.html.haml
--- truth-old/opensource/app/views/statuses/_simple_status.html.haml 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/statuses/_simple_status.html.haml 2024-04-01 14:59:13
@@ -58,6 +58,8 @@
= link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button' do
- if status.distributable?
= fa_icon 'retweet fw'
+ - elsif status.group_visibility?
+ = fa_icon 'users fw'
- elsif status.private_visibility? || status.limited_visibility?
= fa_icon 'lock fw'
- else
diff -ru truth-old/opensource/app/views/user_mailer/status_removed.html.erb truth-new/opensource/app/views/user_mailer/status_removed.html.erb
--- truth-old/opensource/app/views/user_mailer/status_removed.html.erb 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/user_mailer/status_removed.html.erb 2024-04-01 14:59:13
@@ -54,7 +54,7 @@
<table cellpadding="0" cellspacing="0" border="0" role="presentation">
<tr>
<td class="leading-full">
- <a href="https://help.truthsocial.com/community/guidelines" class="relative inline-block py-16 px-24 rounded-full text-base font-medium text-center no-underline text-white bg-primary-600 hover:bg-primary-700">
+ <a href="https://help.truthsocial.com/community-guidelines-page" class="relative inline-block py-16 px-24 rounded-full text-base font-medium text-center no-underline text-white bg-primary-600 hover:bg-primary-700">
<!--[if mso]><i style="letter-spacing: 24px; mso-font-width: -100%; mso-text-raise:30px;">&#8202;</i><![endif]-->
<span style="mso-text-raise: 16px;">
<%= t 'user_mailer.warning.review_server_policies' %>
diff -ru truth-old/opensource/app/views/user_mailer/warning.html.erb truth-new/opensource/app/views/user_mailer/warning.html.erb
--- truth-old/opensource/app/views/user_mailer/warning.html.erb 2022-06-08 09:15:38
+++ truth-new/opensource/app/views/user_mailer/warning.html.erb 2024-04-01 14:59:13
@@ -74,7 +74,7 @@
<table cellpadding="0" cellspacing="0" border="0" role="presentation">
<tr>
<td class="leading-full">
- <a href="https://help.truthsocial.com/community/guidelines" class="relative inline-block py-16 px-24 rounded-full text-base font-medium text-center no-underline text-white bg-primary-600 hover:bg-primary-700">
+ <a href="https://help.truthsocial.com/community-guidelines-page" class="relative inline-block py-16 px-24 rounded-full text-base font-medium text-center no-underline text-white bg-primary-600 hover:bg-primary-700">
<!--[if mso]><i style="letter-spacing: 24px; mso-font-width: -100%; mso-text-raise:30px;">&#8202;</i><![endif]-->
<span style="mso-text-raise: 16px;">
<%= t 'user_mailer.warning.review_server_policies' %>
diff -ru truth-old/opensource/app/workers/account_deletion_worker.rb truth-new/opensource/app/workers/account_deletion_worker.rb
--- truth-old/opensource/app/workers/account_deletion_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/account_deletion_worker.rb 2024-04-01 14:59:13
@@ -5,10 +5,18 @@
sidekiq_options queue: 'pull', lock: :until_executed
- def perform(account_id, options = {})
+ def perform(account_id, deleted_by_id, options = {})
reserve_username = options.with_indifferent_access.fetch(:reserve_username, true)
skip_activitypub = options.with_indifferent_access.fetch(:skip_activitypub, false)
- DeleteAccountService.new.call(Account.find(account_id), reserve_username: reserve_username, skip_activitypub: skip_activitypub, reserve_email: false)
+ deletion_type = options.with_indifferent_access.fetch(:deletion_type, 'unknown')
+ DeleteAccountService.new.call(
+ Account.find(account_id),
+ deleted_by_id,
+ deletion_type: deletion_type,
+ reserve_email: false,
+ reserve_username: reserve_username,
+ skip_activitypub: skip_activitypub,
+ )
rescue ActiveRecord::RecordNotFound
true
end
diff -ru truth-old/opensource/app/workers/activitypub/delivery_worker.rb truth-new/opensource/app/workers/activitypub/delivery_worker.rb
--- truth-old/opensource/app/workers/activitypub/delivery_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/activitypub/delivery_worker.rb 2024-04-01 14:59:13
@@ -39,12 +39,7 @@
Request.new(:post, @inbox_url, body: @json, http_client: http_client).tap do |request|
request.on_behalf_of(@source_account, :uri, sign_with: @options[:sign_with])
request.add_headers(HEADERS)
- request.add_headers({ 'Collection-Synchronization' => synchronization_header }) if ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] != 'true' && @options[:synchronize_followers]
end
- end
-
- def synchronization_header
- "collectionId=\"#{account_followers_url(@source_account)}\", digest=\"#{@source_account.remote_followers_hash(inbox_url_prefix)}\", url=\"#{account_followers_synchronization_url(@source_account)}\""
end
def inbox_url_prefix
Only in truth-old/opensource/app/workers/activitypub: processing_worker.rb
diff -ru truth-old/opensource/app/workers/admin/account_deletion_worker.rb truth-new/opensource/app/workers/admin/account_deletion_worker.rb
--- truth-old/opensource/app/workers/admin/account_deletion_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/admin/account_deletion_worker.rb 2024-04-01 14:59:13
@@ -5,8 +5,15 @@
sidekiq_options queue: 'pull'
- def perform(account_id)
- DeleteAccountService.new.call(Account.find(account_id), reserve_username: true, reserve_email: true)
+ def perform(account_id, deleted_by_id)
+ DeleteAccountService.new.call(
+ Account.find(account_id),
+ deleted_by_id,
+ deletion_type: 'worker_admin_account_deletion',
+ reserve_username: true,
+ reserve_email: true,
+ skip_activitypub: true,
+ )
rescue ActiveRecord::RecordNotFound
true
end
Only in truth-new/opensource/app/workers/admin: ad_worker.rb
Only in truth-new/opensource/app/workers/admin: group_deletion_worker.rb
Only in truth-new/opensource/app/workers/admin: media_privatization_worker.rb
Only in truth-new/opensource/app/workers/admin: media_publication_worker.rb
Only in truth-new/opensource/app/workers/concerns: image_migration.rb
Only in truth-new/opensource/app/workers/concerns: track_ad_impressions_concern.rb
Only in truth-new/opensource/app/workers: disabled_user_refollow_worker.rb
Only in truth-new/opensource/app/workers: disabled_user_unfollow_worker.rb
diff -ru truth-old/opensource/app/workers/distribution_worker.rb truth-new/opensource/app/workers/distribution_worker.rb
--- truth-old/opensource/app/workers/distribution_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/distribution_worker.rb 2024-04-05 09:07:34
@@ -25,13 +25,8 @@
end
def send_to_bailey(status_id)
- if @status.account.silenced? || !@status.public_visibility? || @status.reblog?
- rendered = nil
- else
- rendered = InlineRenderer.render(@status, nil, :status)
- rendered = Oj.dump(event: :update, payload: rendered)
- end
- Redis.current.lpush('elixir:distribution', Oj.dump(status_id: status_id, rendered: rendered))
- Rails.logger.debug("bailey_debug: sending #{rendered.nil? ? 'nil' : 'value'} for rendered for status #{status_id}")
+ QueueManager.enqueue_status_for_author_distribution(status_id)
+ QueueManager.enqueue_status_for_follower_distribution(status_id)
+ Rails.logger.debug("bailey_debug: enqueuing status #{status_id}")
end
end
Only in truth-new/opensource/app/workers: group_acceptance_notify_worker.rb
Only in truth-new/opensource/app/workers: group_deletion_notify_worker.rb
Only in truth-new/opensource/app/workers: group_deletion_worker.rb
Only in truth-new/opensource/app/workers: group_request_notify_worker.rb
Only in truth-new/opensource/app/workers: group_role_change_notify_worker.rb
Only in truth-new/opensource/app/workers: images
Only in truth-new/opensource/app/workers: inspect_link_worker.rb
Only in truth-new/opensource/app/workers: invalidate_account_statuses_worker.rb
Only in truth-new/opensource/app/workers: invalidate_ads_accounts_cache_worker.rb
Only in truth-new/opensource/app/workers: invalidate_ads_accounts_worker.rb
Only in truth-new/opensource/app/workers: invalidate_avatars_carousel_cache_worker.rb
Only in truth-new/opensource/app/workers: invalidate_descendants_cache_worker.rb
diff -ru truth-old/opensource/app/workers/invalidate_follow_cache_worker.rb truth-new/opensource/app/workers/invalidate_follow_cache_worker.rb
--- truth-old/opensource/app/workers/invalidate_follow_cache_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/invalidate_follow_cache_worker.rb 2024-04-01 14:59:13
@@ -5,8 +5,8 @@
include Redisable
def perform(source_account_id, target_account_id, whale)
- Rails.cache.delete("relationship:#{source_account_id}:#{target_account_id}")
- Rails.cache.delete("relationship:#{target_account_id}:#{source_account_id}")
+ Rails.cache.delete("relationship/#{source_account_id}/#{target_account_id}")
+ Rails.cache.delete("relationship/#{target_account_id}/#{source_account_id}")
redis.del("whale:following:#{source_account_id}") if whale
end
end
Only in truth-new/opensource/app/workers: invalidate_group_relationship_cache_worker.rb
Only in truth-new/opensource/app/workers: ios_device_check
diff -ru truth-old/opensource/app/workers/link_crawl_worker.rb truth-new/opensource/app/workers/link_crawl_worker.rb
--- truth-old/opensource/app/workers/link_crawl_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/link_crawl_worker.rb 2024-04-01 14:59:13
@@ -5,8 +5,8 @@
sidekiq_options queue: 'pull', retry: 5
- def perform(status_id, url = nil)
- FetchLinkCardService.new.call(Status.find(status_id), url)
+ def perform(status_id, url = nil, domain = nil)
+ FetchLinkCardService.new.call(Status.find(status_id), url, domain)
rescue ActiveRecord::RecordNotFound
true
end
diff -ru truth-old/opensource/app/workers/local_notification_worker.rb truth-new/opensource/app/workers/local_notification_worker.rb
--- truth-old/opensource/app/workers/local_notification_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/local_notification_worker.rb 2024-04-01 14:59:13
@@ -3,6 +3,8 @@
class LocalNotificationWorker
include Sidekiq::Worker
+ sidekiq_options retry: 1
+
def perform(receiver_account_id, activity_id = nil, activity_class_name = nil, type = nil)
if activity_id.nil? && activity_class_name.nil?
activity = Mention.find(receiver_account_id)
@@ -13,7 +15,5 @@
end
NotifyService.new.call(receiver, type || activity_class_name.underscore, activity)
- rescue ActiveRecord::RecordNotFound
- true
end
end
Only in truth-new/opensource/app/workers/mobile: channel_notification_queueing_worker.rb
Only in truth-new/opensource/app/workers/mobile: channel_notification_worker.rb
Only in truth-new/opensource/app/workers/mobile: marketing_notification_queueing_worker.rb
diff -ru truth-old/opensource/app/workers/mobile/marketing_notification_worker.rb truth-new/opensource/app/workers/mobile/marketing_notification_worker.rb
--- truth-old/opensource/app/workers/mobile/marketing_notification_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/mobile/marketing_notification_worker.rb 2024-04-01 14:59:13
@@ -7,40 +7,59 @@
TTL = 8.hours.to_s
URGENCY = 'normal'
+ IOS_PLATFORM = 1
+ ANDROID_PLATFORM = 2
- def self.queue_notifications(user_ids:, message:, url:)
- self.push_bulk(user_ids) do |user_id|
- [user_id, message, url]
+ def self.queue_notifications(message:, url:, mark_id: nil)
+ tester_platform = IOS_PLATFORM
+ tester_user_ids = if mark_id.present?
+ ENV.fetch('MARK_ID_PUSH_NOTIFICATIONS_USER_IDS', '')
+ .split(',')
+ .map(&:to_i)
+ .reject(&:zero?)
+ end
+
+ if tester_user_ids.present?
+ tester_tokens = MarketingPushSubscription
+ .select(:device_token)
+ .distinct
+ .joins('inner join users on users.id = web_push_subscriptions.user_id')
+ .where(users: { approved: true, disabled: false, id: tester_user_ids }, platform: tester_platform)
+ .where.not(device_token: nil)
+ .to_a
+ .pluck(:device_token)
+ perform_async(tester_tokens, message, url, tester_platform, mark_id)
end
+
+ [IOS_PLATFORM, ANDROID_PLATFORM].each do |platform|
+ tokens = MarketingPushSubscription
+ .select(:device_token)
+ .distinct
+ .joins('inner join users on users.id = web_push_subscriptions.user_id')
+ .where(users: { approved: true, disabled: false }, platform: platform)
+ .where.not(device_token: nil)
+ tokens = tokens.where.not(users: { id: tester_user_ids }) if platform == tester_platform && tester_user_ids.present?
+ tokens.find_in_batches(batch_size: 2_000) do |batch|
+ perform_async(batch.pluck(:device_token), message, url, platform, nil)
+ end
+ end
end
- def perform(user_id, message, url)
- subscriptions = Web::PushSubscription.where(user_id: user_id)
- endpoint = ENV.fetch('MOBILE_NOTIFICATION_ENDPOINT')
+ def perform(tokens, message, url, platform, mark_id)
+ endpoint = ENV.fetch('MOBILE_NOTIFICATION_ENDPOINT')
+ extend = [{ key: 'truthLink', val: url }, { key: 'category', val: 'marketing' }]
+ extend.push({ key: 'markId', val: mark_id }) if mark_id.present?
+ msg = { token: tokens, category: 'invite', platform: platform, message: message, extend: extend }
+ msg[:mutable_content] = true if platform == IOS_PLATFORM
+ body = { 'notifications' => [msg] }.to_json
- # return unless endpoint.present? && subscriptions.any? && message && url
+ return unless endpoint.present? && tokens.any? && message && url
- subscriptions.each do |subscription|
- payload = push_notification_json(subscription.device_token, message, url)
- request_pool.with(Addressable::URI.parse(endpoint).normalized_site) do |http_client|
- request = Request.new(:post, endpoint, body: payload, http_client: http_client)
-
- request.add_headers(
- 'Content-Type' => 'application/octet-stream'
- )
-
- request.perform do |response|
- # If the server responds with an error in the 4xx range
- # that isn't about rate-limiting or timeouts, we can
- # assume that the subscription is invalid or expired
- # and must be removed
-
- if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
- subscription.destroy!
- elsif !(200...300).cover?(response.code)
- raise Mastodon::UnexpectedResponseError, response
- end
- end
+ request_pool.with(Addressable::URI.parse(endpoint).normalized_site) do |http_client|
+ request = Request.new(:post, endpoint, body: body, http_client: http_client)
+ request.add_headers('Content-Type' => 'application/octet-stream')
+ request.perform do |response|
+ raise Mastodon::UnexpectedResponseError, response unless (200...300).cover?(response.code)
end
end
rescue ActiveRecord::RecordNotFound
@@ -49,11 +68,12 @@
private
- def push_notification_json(token, message, url)
- { 'notifications' => [{token: [token], category: 'invite', platform: 1, message: message, extend: [{key: 'truthLink', val: url}]}] }.to_json
- end
-
def request_pool
RequestPool.current
end
+end
+
+class MarketingPushSubscription < ApplicationRecord
+ self.table_name = 'web_push_subscriptions'
+ self.primary_key = :device_token
end
diff -ru truth-old/opensource/app/workers/mobile/push_notification_worker.rb truth-new/opensource/app/workers/mobile/push_notification_worker.rb
--- truth-old/opensource/app/workers/mobile/push_notification_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/mobile/push_notification_worker.rb 2024-04-12 09:09:08
@@ -18,6 +18,7 @@
return unless @endpoint.present? && @notification.activity.present? && @subscription.pushable?(@notification)
payload = push_notification_json
+
request_pool.with(Addressable::URI.parse(@endpoint).normalized_site) do |http_client|
request = Request.new(:post, @endpoint, body: payload, http_client: http_client)
@@ -31,9 +32,7 @@
# assume that the subscription is invalid or expired
# and must be removed
- if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
- @subscription.destroy!
- elsif !(200...300).cover?(response.code)
+ if !(200...300).cover?(response.code)
raise Mastodon::UnexpectedResponseError, response
end
end
diff -ru truth-old/opensource/app/workers/poll_expiration_notify_worker.rb truth-new/opensource/app/workers/poll_expiration_notify_worker.rb
--- truth-old/opensource/app/workers/poll_expiration_notify_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/poll_expiration_notify_worker.rb 2024-04-01 15:02:56
@@ -10,12 +10,11 @@
# Notify poll owner and remote voters
if poll.local?
- ActivityPub::DistributePollUpdateWorker.perform_async(poll.status.id)
NotifyService.new.call(poll.account, :poll, poll)
end
# Notify local voters
- poll.votes.includes(:account).group(:account_id).select(:account_id).map(&:account).select(&:local?).each do |account|
+ PollVote.where(poll_id: poll_id).includes(:account).group(:account_id).select(:account_id).map(&:account).select(&:local?).each do |account|
NotifyService.new.call(account, :poll, poll)
end
rescue ActiveRecord::RecordNotFound
Only in truth-new/opensource/app/workers: process_mention_notifications_worker.rb
Only in truth-new/opensource/app/workers: push_chat_message_worker.rb
Only in truth-new/opensource/app/workers: reblog_removal_worker.rb
Only in truth-new/opensource/app/workers/scheduler: device_verification_cleanup_worker.rb
diff -ru truth-old/opensource/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb truth-new/opensource/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb
--- truth-old/opensource/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb 2024-04-01 14:59:13
@@ -6,7 +6,7 @@
sidekiq_options retry: 0
def perform
- Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
+ OauthAccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
SystemKey.expired.delete_all
end
Only in truth-old/opensource/app/workers/scheduler: pghero_scheduler.rb
Only in truth-new/opensource/app/workers/scheduler: refresh_receipt_scheduler.rb
Only in truth-new/opensource/app/workers/scheduler: tv_create_program_records_scheduler.rb
Only in truth-new/opensource/app/workers/scheduler: tv_refetch_channels_list_scheduler.rb
diff -ru truth-old/opensource/app/workers/scheduler/user_cleanup_scheduler.rb truth-new/opensource/app/workers/scheduler/user_cleanup_scheduler.rb
--- truth-old/opensource/app/workers/scheduler/user_cleanup_scheduler.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/scheduler/user_cleanup_scheduler.rb 2024-04-01 14:59:13
@@ -8,6 +8,7 @@
def perform
clean_unconfirmed_accounts!
clean_suspended_accounts!
+ # clean_suspended_groups! # Revisit once we have a deletion strategy
end
private
@@ -20,8 +21,14 @@
end
def clean_suspended_accounts!
- AccountDeletionRequest.where('created_at <= ?', AccountDeletionRequest::DELAY_TO_DELETION.ago).reorder(nil).find_each do |deletion_request|
- Admin::AccountDeletionWorker.perform_async(deletion_request.account_id)
+ AccountDeletionRequest.where('created_at <= ?', AccountDeletionRequest::DELAY_TO_DELETION.ago).where(Account.where('account_deletion_requests.account_id = accounts.id and accounts.suspended_at <= ?', AccountDeletionRequest::DELAY_TO_DELETION.ago).arel.exists).reorder(nil).find_each do |deletion_request|
+ Admin::AccountDeletionWorker.perform_async(deletion_request.account_id, -99)
end
end
+
+ # def clean_suspended_groups!
+ # GroupDeletionRequest.where('created_at <= ?', GroupDeletionRequest::DELAY_TO_DELETION.ago).reorder(nil).find_each do |deletion_request|
+ # Admin::GroupDeletionWorker.perform_async(deletion_request.group_id)
+ # end
+ # end
end
Only in truth-new/opensource/app/workers: status_moderated_event_worker.rb
Only in truth-new/opensource/app/workers: sync_avatars_seen_accounts_worker.rb
Only in truth-new/opensource/app/workers: sync_groups_seen_worker.rb
Only in truth-new/opensource/app/workers: sync_seen_feeds_worker.rb
Only in truth-new/opensource/app/workers: track_revcontent_ad_impressions_worker.rb
Only in truth-new/opensource/app/workers: track_rumble_ad_impressions_worker.rb
Only in truth-new/opensource/app/workers: tv_accounts_create_worker.rb
Only in truth-new/opensource/app/workers: tv_accounts_login_worker.rb
Only in truth-new/opensource/app/workers: tv_create_tv_program_status_worker.rb
Only in truth-new/opensource/app/workers: tv_program_reminder_notification_worker.rb
Only in truth-new/opensource/app/workers: upload_video_chat_worker.rb
Only in truth-new/opensource/app/workers: upload_video_status_worker.rb
Only in truth-new/opensource/app/workers: video_polling_worker.rb
Only in truth-new/opensource/app/workers: video_preview_worker.rb
Only in truth-old/opensource/app/workers: video_upload_worker.rb
diff -ru truth-old/opensource/app/workers/web/push_notification_worker.rb truth-new/opensource/app/workers/web/push_notification_worker.rb
--- truth-old/opensource/app/workers/web/push_notification_worker.rb 2022-06-08 09:15:38
+++ truth-new/opensource/app/workers/web/push_notification_worker.rb 2024-04-01 14:59:13
@@ -3,7 +3,7 @@
class Web::PushNotificationWorker
include Sidekiq::Worker
- sidekiq_options queue: 'push', retry: 5
+ sidekiq_options queue: 'push', retry: false
TTL = 48.hours.to_s
URGENCY = 'normal'
diff -ru truth-old/opensource/app.json truth-new/opensource/app.json
--- truth-old/opensource/app.json 2022-06-08 09:15:38
+++ truth-new/opensource/app.json 2024-04-01 14:59:13
@@ -93,10 +93,16 @@
}
],
"scripts": {
- "postdeploy": "bundle exec rails db:migrate && bundle exec rails db:seed"
+ "postdeploy": "bundle exec rails db:schema:load && bundle exec rails db:seed"
},
"addons": [
"heroku-postgresql",
"heroku-redis"
- ]
+ ],
+ "dokku": {
+ "plugins": [
+ "postgres",
+ "redis"
+ ]
+ }
}
Only in truth-new/opensource/bin: ci-pre-deploy
Only in truth-new/opensource/bin: rmq_status_moderated_event_worker
Only in truth-new/opensource/bin: sql-injection-check
diff -ru truth-old/opensource/brakeman-output.json truth-new/opensource/brakeman-output.json
--- truth-old/opensource/brakeman-output.json 2022-06-08 09:18:29
+++ truth-new/opensource/brakeman-output.json 2024-04-01 14:59:13
@@ -1,11 +1,11 @@
{
"scan_info": {
- "app_path": "/builds/tmediatech/social-v1",
+ "app_path": "/Users/markmorales/Code/social-v1_groups",
"rails_version": "6.1.3.2",
"security_warnings": 0,
- "start_time": "2022-06-08 13:18:11 +0000",
- "end_time": "2022-06-08 13:18:29 +0000",
- "duration": 18.243194352,
+ "start_time": "2023-07-17 16:03:50 -0500",
+ "end_time": "2023-07-17 16:04:07 -0500",
+ "duration": 17.111335,
"checks_performed": [
"BasicAuth",
"BasicAuthTimingAttack",
@@ -80,9 +80,9 @@
"XMLDoS",
"YAMLParsing"
],
- "number_of_controllers": 211,
- "number_of_models": 122,
- "number_of_templates": 202,
+ "number_of_controllers": 286,
+ "number_of_models": 173,
+ "number_of_templates": 205,
"ruby_version": "2.7.2",
"brakeman_version": "5.0.1"
},
@@ -97,7 +97,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/report.rb",
- "line": 118,
+ "line": 121,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "Admin::ActionLog.from(\"(#{[Admin::ActionLog.where(:target_type => \"Report\", :target_id => id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Account\", :target_id => target_account_id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Status\", :target_id => status_ids, :created_at => ((created_at..updated_at))).unscope(:order)].map do\n \"(#{query.to_sql})\"\n end.join(\" UNION ALL \")}) AS admin_action_logs\")",
"render_path": null,
@@ -110,13 +110,32 @@
"confidence": "High"
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "0a8e1b95121cc27c9eccfa472297b9c738b55e8511a902dfb0cd6c0861cf4a29",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/groups_controller.rb",
+ "line": 186,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:role, :account_ids => ([]), :tags => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::GroupsController",
+ "method": "resource_params"
+ },
+ "user_input": ":role",
+ "confidence": "Medium"
+ },
+ {
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "19df3740b8d02a9fe0eb52c939b4b87d3a2a591162a6adfa8d64e9c26aeebe6d",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/status.rb",
- "line": 107,
+ "line": 131,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "result.joins(\"INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")",
"render_path": null,
@@ -148,6 +167,25 @@
"confidence": "High"
},
{
+ "warning_type": "SQL Injection",
+ "warning_code": 0,
+ "fingerprint": "3373360caca1c1ecad29d946fe605fb60d1ae163d68685c01faee2e86b7ef160",
+ "check_name": "SQL",
+ "message": "Possible SQL injection",
+ "file": "app/models/status.rb",
+ "line": 503,
+ "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
+ "code": "select(\"*\").from(\"(select s.* from statuses s\\n inner join conversations c on c.id = s.conversation_id\\n inner join conversation_mutes cm on cm.conversation_id = c.id\\n where cm.account_id = #{account_id} and in_reply_to_id is null) as statuses\")",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Status",
+ "method": "muted_conversations_for_account"
+ },
+ "user_input": "account_id",
+ "confidence": "Weak"
+ },
+ {
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "5fad11cd67f905fab9b1d5739d01384a1748ebe78c5af5ac31518201925265a7",
@@ -173,7 +211,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/account.rb",
- "line": 542,
+ "line": 611,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "find_by_sql([\" WITH first_degree AS (\\n SELECT target_account_id\\n FROM follows\\n WHERE account_id = ?\\n UNION ALL\\n SELECT ?\\n )\\n SELECT\\n accounts.*,\\n (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?)\\n WHERE accounts.id IN (SELECT * FROM first_degree)\\n AND #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n GROUP BY accounts.id\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, account.id, account.id, account.id, limit, offset])",
"render_path": null,
@@ -192,7 +230,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/status.rb",
- "line": 112,
+ "line": 136,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "result.joins(\"LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")",
"render_path": null,
@@ -230,7 +268,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/account.rb",
- "line": 511,
+ "line": 580,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "find_by_sql([\" SELECT\\n accounts.*,\\n ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n WHERE #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, limit, offset])",
"render_path": null,
@@ -262,6 +300,25 @@
"confidence": "Medium"
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "ab5035dd1a9f8c3a8d92fb2c37e8fe86fede4f87c91b71aa32e89c9eede602fc",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/notifications_controller.rb",
+ "line": 82,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:account_id, :types => ([]), :exclude_types => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::NotificationsController",
+ "method": "browserable_params"
+ },
+ "user_input": ":account_id",
+ "confidence": "High"
+ },
+ {
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "ba568ac09683f98740f663f3d850c31785900215992e8c090497d359a2563d50",
@@ -300,13 +357,32 @@
"confidence": "High"
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "d6003f47b2ac2aa2a8c7e56d8a78d9490999ec6c6e0b82c353575084f8bd47cd",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/reports_controller.rb",
+ "line": 84,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:account_id, :comment, :forward, :group_id, :external_ad_url, :external_ad_media_url, :external_ad_description, :status_ids => ([]), :rule_ids => ([]), :message_ids => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::ReportsController",
+ "method": "report_params"
+ },
+ "user_input": ":account_id",
+ "confidence": "High"
+ },
+ {
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "e21d8fee7a5805761679877ca35ed1029c64c45ef3b4012a30262623e1ba8bb9",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/account.rb",
- "line": 558,
+ "line": 627,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "find_by_sql([\" SELECT\\n accounts.*,\\n (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)\\n WHERE #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n GROUP BY accounts.id\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, account.id, account.id, limit, offset])",
"render_path": null,
@@ -317,31 +393,12 @@
},
"user_input": "textsearch",
"confidence": "Medium"
- },
- {
- "warning_type": "Mass Assignment",
- "warning_code": 105,
- "fingerprint": "efa3a1b56f8c87aabd679156a2d5fd8185d469562361fb9ca8e5b478ed909906",
- "check_name": "PermitAttributes",
- "message": "Potentially dangerous key allowed for mass assignment",
- "file": "app/controllers/api/v1/reports_controller.rb",
- "line": 45,
- "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
- "code": "params.permit(:account_id, :comment, :forward, :status_ids => ([]), :rule_ids => ([]))",
- "render_path": null,
- "location": {
- "type": "method",
- "class": "Api::V1::ReportsController",
- "method": "report_params"
- },
- "user_input": ":account_id",
- "confidence": "High"
}
],
"errors": [
],
"obsolete": [
-
+ "a9c222f356d8b4b673445f266f86e6bfce87df4e97a25ac36407367ee1269bf4"
]
}
\ No newline at end of file
diff -ru truth-old/opensource/config/application.rb truth-new/opensource/config/application.rb
--- truth-old/opensource/config/application.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/application.rb 2024-04-01 14:59:13
@@ -33,6 +33,9 @@
require_relative '../lib/active_record/database_tasks_extensions'
require_relative '../lib/active_record/batches'
require_relative '../lib/s3/seahorse_extensions'
+require_relative '../lib/sidekiq_unique_jobs/manager_extensions'
+require_relative '../lib/http/request_extensions'
+
require 'prometheus_exporter/client'
Dotenv::Railtie.load
@@ -155,10 +158,12 @@
Doorkeeper::AuthorizationsController.layout 'modal'
Doorkeeper::AuthorizedApplicationsController.layout 'admin'
Doorkeeper::Application.send :include, ApplicationExtension
- Doorkeeper::AccessToken.send :include, AccessTokenExtension
+ OauthAccessToken.send :include, AccessTokenExtension
Devise::FailureApp.send :include, AbstractController::Callbacks
Devise::FailureApp.send :include, HttpAcceptLanguage::EasyAccess
Devise::FailureApp.send :include, Localized
end
+
+ config.active_record.schema_format = :sql
end
end
diff -ru truth-old/opensource/config/brakeman.ignore truth-new/opensource/config/brakeman.ignore
--- truth-old/opensource/config/brakeman.ignore 2022-06-08 09:15:38
+++ truth-new/opensource/config/brakeman.ignore 2024-04-01 14:59:13
@@ -7,7 +7,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/report.rb",
- "line": 118,
+ "line": 121,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "Admin::ActionLog.from(\"(#{[Admin::ActionLog.where(:target_type => \"Report\", :target_id => id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Account\", :target_id => target_account_id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Status\", :target_id => status_ids, :created_at => ((created_at..updated_at))).unscope(:order)].map do\n \"(#{query.to_sql})\"\n end.join(\" UNION ALL \")}) AS admin_action_logs\")",
"render_path": null,
@@ -21,13 +21,33 @@
"note": ""
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "0a8e1b95121cc27c9eccfa472297b9c738b55e8511a902dfb0cd6c0861cf4a29",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/groups_controller.rb",
+ "line": 186,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:role, :account_ids => ([]), :tags => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::GroupsController",
+ "method": "resource_params"
+ },
+ "user_input": ":role",
+ "confidence": "Medium",
+ "note": ""
+ },
+ {
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "19df3740b8d02a9fe0eb52c939b4b87d3a2a591162a6adfa8d64e9c26aeebe6d",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/status.rb",
- "line": 103,
+ "line": 131,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "result.joins(\"INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")",
"render_path": null,
@@ -41,6 +61,46 @@
"note": ""
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "224a66f0b51e79901fae28810d09f30d6cd5a9b45eef4699ecb95dff9bd4aa95",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v2/search_controller.rb",
+ "line": 28,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:type, :offset, :min_id, :max_id, :account_id, :q, :resolve, :exclude_unreviewed, :limit)",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V2::SearchController",
+ "method": "search_params"
+ },
+ "user_input": ":account_id",
+ "confidence": "High",
+ "note": ""
+ },
+ {
+ "warning_type": "SQL Injection",
+ "warning_code": 0,
+ "fingerprint": "3373360caca1c1ecad29d946fe605fb60d1ae163d68685c01faee2e86b7ef160",
+ "check_name": "SQL",
+ "message": "Possible SQL injection",
+ "file": "app/models/status.rb",
+ "line": 503,
+ "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
+ "code": "select(\"*\").from(\"(select s.* from statuses s\\n inner join conversations c on c.id = s.conversation_id\\n inner join conversation_mutes cm on cm.conversation_id = c.id\\n where cm.account_id = #{account_id} and in_reply_to_id is null) as statuses\")",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Status",
+ "method": "muted_conversations_for_account"
+ },
+ "user_input": "account_id",
+ "confidence": "Weak",
+ "note": ""
+ },
+ {
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "5fad11cd67f905fab9b1d5739d01384a1748ebe78c5af5ac31518201925265a7",
@@ -67,7 +127,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/account.rb",
- "line": 525,
+ "line": 611,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "find_by_sql([\" WITH first_degree AS (\\n SELECT target_account_id\\n FROM follows\\n WHERE account_id = ?\\n UNION ALL\\n SELECT ?\\n )\\n SELECT\\n accounts.*,\\n (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?)\\n WHERE accounts.id IN (SELECT * FROM first_degree)\\n AND #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n GROUP BY accounts.id\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, account.id, account.id, account.id, limit, offset])",
"render_path": null,
@@ -87,7 +147,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/status.rb",
- "line": 108,
+ "line": 136,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "result.joins(\"LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")",
"render_path": null,
@@ -103,25 +163,6 @@
{
"warning_type": "Mass Assignment",
"warning_code": 105,
- "fingerprint": "224a66f0b51e79901fae28810d09f30d6cd5a9b45eef4699ecb95dff9bd4aa95",
- "check_name": "PermitAttributes",
- "message": "Potentially dangerous key allowed for mass assignment",
- "file": "app/controllers/api/v2/search_controller.rb",
- "line": 28,
- "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
- "code": "params.permit(:type, :offset, :min_id, :max_id, :account_id, :q, :resolve, :exclude_unreviewed, :limit)",
- "render_path": null,
- "location": {
- "type": "method",
- "class": "Api::V2::SearchController",
- "method": "search_params"
- },
- "user_input": ":account_id",
- "confidence": "High"
- },
- {
- "warning_type": "Mass Assignment",
- "warning_code": 105,
"fingerprint": "874be88fedf4c680926845e9a588d3197765a6ccbfdd76466b44cc00151c612e",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
@@ -146,7 +187,7 @@
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/account.rb",
- "line": 494,
+ "line": 580,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "find_by_sql([\" SELECT\\n accounts.*,\\n ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n WHERE #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, limit, offset])",
"render_path": null,
@@ -180,6 +221,46 @@
"note": ""
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "a9c222f356d8b4b673445f266f86e6bfce87df4e97a25ac36407367ee1269bf4",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/reports_controller.rb",
+ "line": 70,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:account_id, :comment, :forward, :group_id, :status_ids => ([]), :rule_ids => ([]), :message_ids => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::ReportsController",
+ "method": "report_params"
+ },
+ "user_input": ":account_id",
+ "confidence": "High",
+ "note": ""
+ },
+ {
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "ab5035dd1a9f8c3a8d92fb2c37e8fe86fede4f87c91b71aa32e89c9eede602fc",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/notifications_controller.rb",
+ "line": 82,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:account_id, :types => ([]), :exclude_types => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::NotificationsController",
+ "method": "browserable_params"
+ },
+ "user_input": ":account_id",
+ "confidence": "High",
+ "note": ""
+ },
+ {
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "ba568ac09683f98740f663f3d850c31785900215992e8c090497d359a2563d50",
@@ -220,13 +301,33 @@
"note": ""
},
{
+ "warning_type": "Mass Assignment",
+ "warning_code": 105,
+ "fingerprint": "d6003f47b2ac2aa2a8c7e56d8a78d9490999ec6c6e0b82c353575084f8bd47cd",
+ "check_name": "PermitAttributes",
+ "message": "Potentially dangerous key allowed for mass assignment",
+ "file": "app/controllers/api/v1/reports_controller.rb",
+ "line": 84,
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
+ "code": "params.permit(:account_id, :comment, :forward, :group_id, :external_ad_url, :external_ad_media_url, :external_ad_description, :status_ids => ([]), :rule_ids => ([]), :message_ids => ([]))",
+ "render_path": null,
+ "location": {
+ "type": "method",
+ "class": "Api::V1::ReportsController",
+ "method": "report_params"
+ },
+ "user_input": ":account_id",
+ "confidence": "High",
+ "note": ""
+ },
+ {
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "e21d8fee7a5805761679877ca35ed1029c64c45ef3b4012a30262623e1ba8bb9",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/account.rb",
- "line": 541,
+ "line": 627,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "find_by_sql([\" SELECT\\n accounts.*,\\n (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)\\n WHERE #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n GROUP BY accounts.id\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, account.id, account.id, limit, offset])",
"render_path": null,
@@ -238,28 +339,8 @@
"user_input": "textsearch",
"confidence": "Medium",
"note": ""
- },
- {
- "warning_type": "Mass Assignment",
- "warning_code": 105,
- "fingerprint": "efa3a1b56f8c87aabd679156a2d5fd8185d469562361fb9ca8e5b478ed909906",
- "check_name": "PermitAttributes",
- "message": "Potentially dangerous key allowed for mass assignment",
- "file": "app/controllers/api/v1/reports_controller.rb",
- "line": 45,
- "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
- "code": "params.permit(:account_id, :comment, :forward, :status_ids => ([]), :rule_ids => ([]))",
- "render_path": null,
- "location": {
- "type": "method",
- "class": "Api::V1::ReportsController",
- "method": "report_params"
- },
- "user_input": ":account_id",
- "confidence": "High",
- "note": ""
}
],
- "updated": "2022-02-03 12:54:45 -0500",
+ "updated": "2023-07-17 16:08:14 -0500",
"brakeman_version": "5.0.1"
}
diff -ru truth-old/opensource/config/database.yml truth-new/opensource/config/database.yml
--- truth-old/opensource/config/database.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/database.yml 2024-04-01 14:59:13
@@ -35,14 +35,22 @@
connections:
- role: master
blacklist_duration: 0
- url: postgresql://<%= ENV['DB_USER'] || 'truth' %>:<%= ENV['DB_PASS'] || '' %>@<%= ENV['DB_HOST'] || 'localhost' %>:<%= ENV['DB_PORT'] || 5432 %>/<%= ENV['DB_NAME'] || 'truth_production' %>
+ host: <%= ENV['DB_HOST'] || 'localhost' %>
+ port: <%= ENV['DB_PORT'] || 5432 %>
+ database: <%= ENV['DB_NAME'] || 'truth_production' %>
+ username: <%= ENV['DB_USER'] || 'truth' %>
+ password: <%= ENV['DB_PASS'] || '' %>
- role: slave
blacklist_duration: 0
- url: postgresql://<%= ENV['DB_USER'] || 'truth' %>:<%= ENV['DB_PASS'] || '' %>@<%= ENV['DB_HOST_REPLICA'] || 'localhost' %>:<%= ENV['DB_PORT_REPLICA'] || 5433 %>/<%= ENV['DB_NAME'] || 'truth_production' %>
+ host: <%= ENV['DB_HOST_REPLICA'] || 'localhost' %>
+ port: <%= ENV['DB_PORT_REPLICA'] || 5433 %>
+ database: <%= ENV['DB_NAME_REPLICA'] || 'truth_production_ro' %>
+ username: <%= ENV['DB_USER_REPLICA'] || 'truth' %>
+ password: <%= ENV['DB_PASS_REPLICA'] || '' %>
<% else %>
+ host: <%= ENV['DB_HOST'] || 'localhost' %>
+ port: <%= ENV['DB_PORT'] || 5432 %>
database: <%= ENV['DB_NAME'] || 'truth_production' %>
username: <%= ENV['DB_USER'] || 'truth' %>
password: <%= ENV['DB_PASS'] || '' %>
- host: <%= ENV['DB_HOST'] || 'localhost' %>
- port: <%= ENV['DB_PORT'] || 5432 %>
<% end %>
diff -ru truth-old/opensource/config/environment.rb truth-new/opensource/config/environment.rb
--- truth-old/opensource/config/environment.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/environment.rb 2024-04-01 14:59:13
@@ -4,4 +4,7 @@
# Initialize the Rails application.
Rails.application.initialize!
+# Require all proto events & schemas
+Dir[File.expand_path("./lib/proto/**/*.rb")].each { |f| require f }
+
ActiveRecord::SchemaDumper.ignore_tables = ['deprecated_preview_cards']
diff -ru truth-old/opensource/config/environments/development.rb truth-new/opensource/config/environments/development.rb
--- truth-old/opensource/config/environments/development.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/environments/development.rb 2024-04-01 14:59:13
@@ -105,6 +105,7 @@
end
config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109')
+ config.hosts << "#{Rails.configuration.x.use_https ? 'https' : 'http' }://#{Rails.configuration.x.web_domain}"
end
ActiveRecordQueryTrace.enabled = ENV['QUERY_TRACE_ENABLED'] == 'true'
diff -ru truth-old/opensource/config/environments/production.rb truth-new/opensource/config/environments/production.rb
--- truth-old/opensource/config/environments/production.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/environments/production.rb 2024-04-01 14:59:13
@@ -47,7 +47,7 @@
config.force_ssl = true
config.ssl_options = {
redirect: {
- exclude: -> request { request.path.start_with?('/health') || request.headers["Host"].end_with?('.onion') }
+ exclude: -> request { request.path.start_with?('/health') || request.headers["Host"].end_with?('.onion') || request.remote_ip === '127.0.0.1'}
}
}
Only in truth-new/opensource/config: imagemagick
diff -ru truth-old/opensource/config/initializers/chewy.rb truth-new/opensource/config/initializers/chewy.rb
--- truth-old/opensource/config/initializers/chewy.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/chewy.rb 2024-04-01 14:59:13
@@ -1,17 +1,28 @@
-enabled = ENV['ES_ENABLED'] == 'true'
-host = ENV.fetch('ES_HOST') { 'localhost' }
-port = ENV.fetch('ES_PORT') { 9200 }
+enabled = ENV['ES_ENABLED'] == 'true'
+indexing_enabled = ENV.fetch('ES_INDEXING_ENABLED') { ENV['ES_ENABLED'] } == 'true'
+host = ENV.fetch('ES_HOST') { 'localhost' }
+port = ENV.fetch('ES_PORT') { 9200 }
fallback_prefix = ENV.fetch('REDIS_NAMESPACE') { nil }
-prefix = ENV.fetch('ES_PREFIX') { fallback_prefix }
+prefix = ENV.fetch('ES_PREFIX') { fallback_prefix }
+username = ENV.fetch('ES_USER') { nil }
+password = ENV.fetch('ES_PASSWORD') { nil }
Chewy.settings = {
- host: "#{host}:#{port}",
+ host: host,
+ port: port,
prefix: prefix,
enabled: enabled,
+ indexing_enabled: indexing_enabled,
journal: false,
sidekiq: { queue: 'chewy' },
}
+if username && password
+ Chewy.settings[:user] = username
+ Chewy.settings[:password] = password
+end
+
+
# We use our own async strategy even outside the request-response
# cycle, which takes care of checking if ElasticSearch is enabled
# or not. However, mind that for the Rails console, the :urgent
@@ -25,6 +36,10 @@
def enabled?
settings[:enabled]
end
+
+ def indexing_enabled?
+ settings[:indexing_enabled]
+ end
end
end
@@ -33,23 +48,3 @@
# Mastodon is run with hidden services enabled, because
# ElasticSearch is *not* supposed to be accessed through a proxy
Faraday.ignore_env_proxy = true
-
-# Elasticsearch 7.x workaround
-Elasticsearch::Transport::Client.prepend Module.new {
- def search(arguments = {})
- arguments[:rest_total_hits_as_int] = true
- super arguments
- end
-}
-
-Elasticsearch::API::Indices::IndicesClient.prepend Module.new {
- def create(arguments = {})
- arguments[:include_type_name] = true
- super arguments
- end
-
- def put_mapping(arguments = {})
- arguments[:include_type_name] = true
- super arguments
- end
-}
diff -ru truth-old/opensource/config/initializers/content_security_policy.rb truth-new/opensource/config/initializers/content_security_policy.rb
--- truth-old/opensource/config/initializers/content_security_policy.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/content_security_policy.rb 2024-04-01 14:59:13
@@ -19,8 +19,8 @@
#TODO: refactor the maintenance of these URLs
segment_base_url = 'https://cdn.segment.com'
segment_api_url = 'https://api.segment.io'
-segment_script_hashes = ["'sha256-Kru1cRFDRjvkSX3GJVOzPMlesOJPlwl8Yf/vyxi7wnc='",
- "'sha256-SkDGcKd1lxidykiwp0MQl3em4R4qTUyDCyVbFr52Qdo='",
+segment_script_hashes = ["'sha256-Kru1cRFDRjvkSX3GJVOzPMlesOJPlwl8Yf/vyxi7wnc='",
+ "'sha256-SkDGcKd1lxidykiwp0MQl3em4R4qTUyDCyVbFr52Qdo='",
"'sha256-CZKu4Ofm+PztnJbExQzfZGKk50F7ttkRpdQxduN4lCA='"
]
@@ -59,14 +59,3 @@
Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
Rails.application.config.content_security_policy_nonce_directives = %w(style-src)
-
-Rails.application.reloader.to_prepare do
- PgHero::HomeController.content_security_policy do |p|
- p.script_src :self, :unsafe_inline, assets_host
- p.style_src :self, :unsafe_inline, assets_host
- end
-
- PgHero::HomeController.after_action do
- request.content_security_policy_nonce_generator = nil
- end
-end
diff -ru truth-old/opensource/config/initializers/cors.rb truth-new/opensource/config/initializers/cors.rb
--- truth-old/opensource/config/initializers/cors.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/cors.rb 2024-04-01 14:59:13
@@ -25,7 +25,16 @@
headers: :any,
methods: [:post, :put, :delete, :get, :patch, :options],
credentials: false,
- expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id']
+ expose: [
+ 'Link',
+ 'X-RateLimit-Reset',
+ 'X-RateLimit-Limit',
+ 'X-RateLimit-Remaining',
+ 'X-Request-Id',
+ 'X-Unread-Messages-Count',
+ 'X-Total-Count',
+ 'X-Truth-Ad-Indexes'
+ ]
resource '/oauth/token',
headers: :any,
methods: [:post],
diff -ru truth-old/opensource/config/initializers/doorkeeper.rb truth-new/opensource/config/initializers/doorkeeper.rb
--- truth-old/opensource/config/initializers/doorkeeper.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/doorkeeper.rb 2024-04-01 14:59:13
@@ -2,6 +2,8 @@
# Change the ORM that doorkeeper will use (needs plugins)
orm :active_record
+ access_token_class "OauthAccessToken"
+
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
current_user || redirect_to(new_user_session_url)
@@ -11,22 +13,28 @@
username = request.params[:username]
if (username.present?)
- user = if username.include?("@")
+ username.strip!
+ user = if username =~ URI::MailTo::EMAIL_REGEXP
# Emails on the user table are all stored in lowercase, so this should allow us to login with case-insensitive email
User.find_by(email: username.downcase)
- else
+ elsif username.first == '@'
# Unlike emails usernames are stored as they are entered so we need to query case insensitive
+ Account.ci_find_by_username(username[1..-1])&.user
+ else
Account.ci_find_by_username(username)&.user
end
user = nil unless user&.valid_password?(request.params[:password])
elsif request.params[:mfa_token].present?
- user = User.get_user_from_token(request.params[:mfa_token])
+ mfa_token = params[:mfa_token].strip
+ code = params[:code].strip
+ user = User.get_user_from_token(mfa_token)
+
if user.present?
- user = nil unless user.validate_user_token(request.params[:mfa_token])
- user = nil unless (user.validate_and_consume_otp!(request.params[:code]) ||
- user.invalidate_otp_backup_code!(request.params[:code]))
+ user = nil unless user.validate_user_token(mfa_token)
+ user = nil unless (user.validate_and_consume_otp!(code) ||
+ user.invalidate_otp_backup_code!(code))
end
else
user = nil
@@ -123,7 +131,8 @@
:'admin:write',
:'admin:write:accounts',
:'admin:write:reports',
- :crypto
+ :crypto,
+ :ads
# Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
diff -ru truth-old/opensource/config/initializers/filter_parameter_logging.rb truth-new/opensource/config/initializers/filter_parameter_logging.rb
--- truth-old/opensource/config/initializers/filter_parameter_logging.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/filter_parameter_logging.rb 2024-04-01 14:59:13
@@ -1,4 +1,4 @@
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
-Rails.application.config.filter_parameters += [:password, :sms, :private_key, :public_key, :otp_attempt]
+Rails.application.config.filter_parameters += [:password, :sms, :private_key, :public_key, :otp_attempt, :token]
diff -ru truth-old/opensource/config/initializers/makara.rb truth-new/opensource/config/initializers/makara.rb
--- truth-old/opensource/config/initializers/makara.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/makara.rb 2024-04-01 14:59:13
@@ -13,4 +13,4 @@
end
end
-Object.send :include, ReadFromReplica
\ No newline at end of file
+Object.send :include, ReadFromReplica
diff -ru truth-old/opensource/config/initializers/paperclip.rb truth-new/opensource/config/initializers/paperclip.rb
--- truth-old/opensource/config/initializers/paperclip.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/paperclip.rb 2024-04-01 14:59:13
@@ -11,7 +11,7 @@
end
end
-Paperclip.interpolates :prefix_path do |attachment, style|
+Paperclip.interpolates :prefix_path do |attachment, _style|
if attachment.storage_schema_version >= 1 && attachment.instance.respond_to?(:local?) && !attachment.instance.local?
'cache' + File::SEPARATOR
else
@@ -19,7 +19,7 @@
end
end
-Paperclip.interpolates :prefix_url do |attachment, style|
+Paperclip.interpolates :prefix_url do |attachment, _style|
if attachment.storage_schema_version >= 1 && attachment.instance.respond_to?(:local?) && !attachment.instance.local?
'cache/'
else
@@ -61,8 +61,8 @@
s3_options: {
signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' },
- http_open_timeout: ENV.fetch('S3_OPEN_TIMEOUT'){ '5' }.to_i,
- http_read_timeout: ENV.fetch('S3_READ_TIMEOUT'){ '5' }.to_i,
+ http_open_timeout: ENV.fetch('S3_OPEN_TIMEOUT') { '5' }.to_i,
+ http_read_timeout: ENV.fetch('S3_READ_TIMEOUT') { '5' }.to_i,
http_idle_timeout: 5,
retry_limit: 0,
http_proxy: nil,
@@ -72,7 +72,7 @@
if ENV.has_key?('S3_ENDPOINT')
Paperclip::Attachment.default_options[:s3_options].merge!(
endpoint: ENV['S3_ENDPOINT'],
- force_path_style: ENV['S3_OVERRIDE_PATH_STYLE'] != 'true',
+ force_path_style: ENV['S3_OVERRIDE_PATH_STYLE'] != 'true'
)
Paperclip::Attachment.default_options[:url] = ':s3_path_url'
@@ -109,7 +109,7 @@
Paperclip::Attachment.default_options.merge!(
storage: :filesystem,
path: File.join(ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')), ':prefix_path:class', ':attachment', ':id_partition', ':style', ':filename'),
- url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:prefix_url:class/:attachment/:id_partition/:style/:filename',
+ url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:prefix_url:class/:attachment/:id_partition/:style/:filename'
)
end
@@ -126,4 +126,11 @@
class NetworkingError < StandardError; end
end
end
+end
+
+# Set our ImageMagick security policy, but allow admins to override it
+ENV['MAGICK_CONFIGURE_PATH'] = begin
+ imagemagick_config_paths = ENV.fetch('MAGICK_CONFIGURE_PATH', '').split(File::PATH_SEPARATOR)
+ imagemagick_config_paths << Rails.root.join('config', 'imagemagick').expand_path.to_s
+ imagemagick_config_paths.join(File::PATH_SEPARATOR)
end
diff -ru truth-old/opensource/config/initializers/rack_attack.rb truth-new/opensource/config/initializers/rack_attack.rb
--- truth-old/opensource/config/initializers/rack_attack.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/rack_attack.rb 2024-04-01 14:59:13
@@ -7,6 +7,10 @@
def authenticated_token
return @token if defined?(@token)
+ if Rails.env.production? && path.start_with?('/api/v1/accounts/verify_credentials')
+ ActiveRecord::Base.connection.stick_to_master!(false)
+ end
+
@token = Doorkeeper::OAuth::Token.authenticate(
Doorkeeper::Grape::AuthorizationDecorator.new(self),
*Doorkeeper.configuration.access_token_methods
@@ -14,7 +18,7 @@
end
def remote_ip
- @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
+ @remote_ip ||= (@env['action_dispatch.remote_ip'] || ip).to_s
end
def authenticated_user_id
@@ -40,10 +44,9 @@
def private_address?(address)
PrivateAddressCheck.private_address?(IPAddr.new(address))
end
-
end
- unless ENV["SKIP_IP_RATE_LIMITING"] == "true"
+ unless ENV['SKIP_IP_RATE_LIMITING'] == 'true'
Rack::Attack.safelist('allow from private') do |req|
req.private_address?(req.remote_ip)
end
@@ -97,20 +100,19 @@
throttle('throttle_password_resets/email', limit: 5, period: 30.minutes) do |req|
(req.params.dig('user', 'email').presence if req.post? && req.path == '/auth/password') ||
- (req.params.dig('email').presence if req.post? && req.path == '/api/pleroma/change_password') ||
- (req.params.dig('email').presence if req.post? && req.path == '/api/v1/truth/password_reset/request')
-
+ (req.params.dig('email').presence if req.post? && req.path == '/api/pleroma/change_password') ||
+ (req.params.dig('email').presence if req.post? && req.path == '/api/v1/truth/password_reset/request')
end
throttle('throttle_email_confirmations/ip', limit: 25, period: 5.minutes) do |req|
- req.remote_ip if ((req.post? && %w(/auth/confirmation /api/v1/emails/confirmations).include?(req.path)) ||
- (req.get? && req.path.include?('api/v1/truth/email/confirm')))
+ req.remote_ip if (req.post? && %w(/auth/confirmation /api/v1/emails/confirmations).include?(req.path)) ||
+ (req.get? && req.path.include?('api/v1/truth/email/confirm'))
end
throttle('throttle_email_confirmations/email', limit: 5, period: 30.minutes) do |req|
if req.post? && req.path == '/auth/confirmation'
req.params.dig('user', 'email').presence
- elsif ((req.post? && req.path == '/api/v1/emails/confirmations') || (req.get? && req.path == '/api/v1/truth/email/confirm'))
+ elsif (req.post? && req.path == '/api/v1/emails/confirmations') || (req.get? && req.path == '/api/v1/truth/email/confirm')
req.authenticated_user_id
end
end
@@ -121,8 +123,23 @@
throttle('throttle_login_attempts/email', limit: 25, period: 1.hour) do |req|
(req.session[:attempt_user_id] || req.params.dig('user', 'email').presence if req.post? && req.path == '/auth/sign_in') ||
- (req.params.dig('username').presence if req.post? && req.path == '/oauth/token') ||
- (req.params.dig('mfa_token').presence if req.post? && req.path == '/oauth/mfa/challenge')
+ (req.params.dig('username').presence if req.post? && req.path == '/oauth/token') ||
+ (req.params.dig('mfa_token').presence if req.post? && req.path == '/oauth/mfa/challenge')
+ end
+
+ API_CHAT_MESSAGE_REGEX = /\A\/api\/v1\/pleroma\/chats\/[\d]+\/messages/.freeze
+ API_CHAT_MESSAGE_REACTION_REGEX = /\A\/api\/v1\/pleroma\/chats\/[\d]+\/messages\/[\d]+\/reactions/.freeze
+
+ throttle('throttle_chat_messages', limit: ChatMessage::MAX_MESSAGES_PER_MIN, period: 1.minute) do |req|
+ req.remote_ip if req.post? && req.path.match?(API_CHAT_MESSAGE_REGEX)
+ end
+
+ throttle('throttle_chat_message_reactions', limit: ChatMessageReaction::MAX_MESSAGES_PER_MIN, period: 1.minute) do |req|
+ req.remote_ip if req.post? && req.path.match?(API_CHAT_MESSAGE_REACTION_REGEX)
+ end
+
+ throttle('throttle_app_attest_attestations', limit: 11, period: 1.second) do |req|
+ req.remote_ip if req.path == '/api/v1/truth/ios_device_check/rate_limit'
end
self.throttled_response = lambda do |env|
@@ -139,4 +156,4 @@
[429, headers, [{ error: I18n.t('errors.429') }.to_json]]
end
end
-end
\ No newline at end of file
+end
diff -ru truth-old/opensource/config/initializers/rack_attack_logging.rb truth-new/opensource/config/initializers/rack_attack_logging.rb
--- truth-old/opensource/config/initializers/rack_attack_logging.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/rack_attack_logging.rb 2023-05-05 13:42:02
@@ -5,4 +5,7 @@
Rails.logger.info("Rate limit hit by rack-attack (#{req.env['rack.attack.match_type']} #{req.env['rack.attack.matched']}): #{req.ip} #{req.request_method} #{req.fullpath} #{req.authenticated_user_id}")
+ redis_key = "rate_limit:#{DateTime.current.to_date}"
+ redis_element_key = "#{req.authenticated_user_id}-#{req.ip}"
+ Redis.current.zincrby(redis_key, 1, redis_element_key)
end
Only in truth-new/opensource/config/initializers: rmq_consumer.rb
Only in truth-old/opensource/config/initializers: statsd.rb
diff -ru truth-old/opensource/config/initializers/webauthn.rb truth-new/opensource/config/initializers/webauthn.rb
--- truth-old/opensource/config/initializers/webauthn.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/initializers/webauthn.rb 2024-04-01 14:59:13
@@ -20,5 +20,22 @@
# In this case the default would be "auth.example.com", but you can set it to
# the suffix "example.com"
#
- # config.rp_id = "example.com"
+ config.rp_id = ENV.fetch('SERVER_RP_ID', "truth.social")
+ config.silent_authentication = true
+end
+
+module WebAuthn
+ module AttestationStatement
+ def self.from(format, statement)
+ new_format = format == "apple-appattest" ? "apple" : format
+
+ klass = FORMAT_TO_CLASS[new_format]
+
+ if klass
+ klass.new(statement)
+ else
+ raise(FormatNotSupportedError, "Unsupported attestation format '#{format}'")
+ end
+ end
+ end
end
diff -ru truth-old/opensource/config/locales/en.yml truth-new/opensource/config/locales/en.yml
--- truth-old/opensource/config/locales/en.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/locales/en.yml 2024-04-05 09:17:47
@@ -55,6 +55,8 @@
other: users
user_count_before: Home to
what_is_mastodon: What is Truth?
+ ads:
+ why_copy: We show ads for products and services we think our users might like.
accounts:
choices_html: "%{name}'s choices:"
endorsements_hint: You can endorse people you follow from the web interface, and they will show up here.
@@ -126,7 +128,6 @@
disable_two_factor_authentication: Disable 2FA
disabled: Frozen
display_name: Display name
- location: Location
website: Website
domain: Domain
edit: Edit
@@ -256,6 +257,7 @@
enable_user: Enable User
memorialize_account: Memorialize Account
promote_user: Promote User
+ remove_avatar_group: Remove Group Avatar
remove_avatar_user: Remove Avatar
reopen_report: Reopen Report
reset_password_user: Reset Password
@@ -263,10 +265,12 @@
sensitive_account: Mark the media in your account as sensitive
silence_account: Silence Account
suspend_account: Suspend Account
+ suspend_group: Suspend Group
unassigned_report: Unassign Report
unsensitive_account: Unmark the media in your account as sensitive
unsilence_account: Unsilence Account
unsuspend_account: Unsuspend Account
+ unsuspend_group: Unsuspend Group
update_announcement: Update Announcement
update_custom_emoji: Update Custom Emoji
update_domain_block: Update Domain Block
@@ -299,6 +303,7 @@
enable_user_html: "%{name} enabled login for user %{target}"
memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page"
promote_user_html: "%{name} promoted user %{target}"
+ remove_avatar_group_html: "%{name} removed the avatar for group %{target}"
remove_avatar_user_html: "%{name} removed %{target}'s avatar"
reopen_report_html: "%{name} reopened report %{target}"
reset_password_user_html: "%{name} reset password of user %{target}"
@@ -306,10 +311,12 @@
sensitive_account_html: "%{name} marked %{target}'s media as sensitive"
silence_account_html: "%{name} silenced %{target}'s account"
suspend_account_html: "%{name} suspended %{target}'s account"
+ suspend_group_html: "%{name} suspended the group %{target}"
unassigned_report_html: "%{name} unassigned report %{target}"
unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive"
unsilence_account_html: "%{name} unsilenced %{target}'s account"
unsuspend_account_html: "%{name} unsuspended %{target}'s account"
+ unsuspend_group_html: "%{name} unsuspended the group %{target}"
update_announcement_html: "%{name} updated announcement %{target}"
update_custom_emoji_html: "%{name} updated emoji %{target}"
update_domain_block_html: "%{name} updated domain block for %{target}"
@@ -463,12 +470,20 @@
suppressed: Suppressed
title: Follow recommendations
unsuppress: Restore follow recommendation
- trending_truths:
- description_html: "<strong>Follow recommendations help new users quickly find interesting content</strong>. When a user has not interacted with others enough to form personalized follow recommendations, these accounts are recommended instead. They are re-calculated on a daily basis from a mix of accounts with the highest recent engagements and highest local follower counts for a given language."
- status: Status
- trending: Trending
- all: All
- title: Trending truths
+ group_memberships:
+ title: Group members
+ group_statuses:
+ back_to_group: Back to group page
+ title: Group posts
+ groups:
+ group: Group
+ removed_avatar_msg: Successfully removed this group's avatar image
+ removed_header_msg: Successfully removed this group's header image
+ suspended_msg: Successfully suspended this group
+ suspension_irreversible: The data of this group has been irreversibly deleted. You can unsuspend the group to make it usable but it will not recover any data it previously had.
+ suspension_reversible_hint_html: The group has been suspended, and the data will be fully removed on %{date}. Until then, the group can be restored without any ill effects. If you wish to remove all of the group's data immediately, you can do so below.
+ title: Groups
+ unsuspended_msg: Successfully unsuspended this group
instances:
back_to_all: All
back_to_limited: Limited
@@ -499,6 +514,87 @@
title: Moderation
private_comment: Private comment
public_comment: Public comment
+ rules:
+ content_illegal_activity:
+ text: Illegal activity and behavior
+ subtext: Content that depicts illegal or criminal acts, threats of violence.
+ content_intellectual_infringement:
+ text: Intellectual property infringement
+ subtext: Impersonating another account or business, infringing on intellectual property
+ rights.
+ content_sensitive_content:
+ text: Sensitive Content
+ subtext: Depictions of violence, gore, nudity.
+ content_underage_content:
+ text: Underage content
+ subtext: Sexually explicit content involving underage children.
+ content_prostitution:
+ text: Prostitution
+ subtext: Solicitation or advertising for illegal sexual activity or sex for hire.
+ content_privacy_violations:
+ text: Privacy violations
+ subtext: Violate or post content that violates a person's privacy rights.
+ content_illegal_sales:
+ text: Illegal sales
+ subtext: 'Sale of or promotion of illegal drugs, counterfeit services and goods,
+ or illegal products and services. '
+ content_doxxing:
+ text: Doxxing
+ subtext: Sharing or threatening to share the private information of an individual
+ without their consent or breach of privacy rights of others.
+ content_spam:
+ text: Spam
+ subtext: Fraudulent or malicious content or links, inauthentic engagement, repetitive
+ replies, ReTruths, or direct messages.
+ content_dislike:
+ text: I don't like this content
+ subtext: I dislike this content and/or this user is a troll.
+ account_illegal_activity:
+ text: Illegal activity and behavior
+ subtext: Content that depicts illegal or criminal acts, threats of violence.
+ account_sensitive_content:
+ text: Sensitive Content
+ subtext: Depictions of violence, gore, nudity.
+ account_spam:
+ text: SPAM
+ subtext: Fraudulent content, phishing, or content that is spam or a bot.
+ account_fake_account:
+ text: Fake Account
+ subtext: Improper use of username/account to masquerade as someone else or impersonate another person or entity that is not a parody.
+ account_intellectual_property:
+ text: Intellectual Property
+ subtext: Account name or entity infringes on intellectual property rights without
+ authorization.
+ account_underage_content:
+ text: Underage content
+ subtext: Sexually explicit content involving underage children.
+ account_prostitution:
+ text: Prostitution
+ subtext: Solicitation or advertising for illegal sexual activity or sex for hire.
+ account_privacy_violations:
+ text: Privacy violations
+ subtext: Violate or post content that violates a person's privacy rights.
+ account_troll:
+ text: Troll
+ subtext: This user is a troll and/or I dislike their content.
+ group_illegal_activity:
+ text: Illegal Activity and Behavior
+ subtext: 'This group is participating, encouraging or depicting illegal or criminal acts such as: threats, violence, incitement of violence, doxxing or harrassment.'
+ group_spam:
+ text: Spam
+ subtext: Fraudulent content, phishing or content that is spam or a bot.
+ group_impersonation:
+ text: Impersonation
+ subtext: This group is claiming to represent a person/entity that they aren't authorized to represent.
+ group_underage_content:
+ text: Underage Content
+ subtext: This group has posted sexually explicit content involving underage children.
+ group_prostitution:
+ text: Prostitution
+ subtext: This group is soliciting or advertising for illegal sexual activity or sex for hire.
+ group_privacy_violations:
+ text: Privacy Violations
+ subtext: This group has violated mine or someone else's privacy rights, including doxxing.
title: Federation
total_blocked_by_us: Blocked by us
total_followed_by_them: Followed by them
@@ -840,6 +936,14 @@
hint_html: "<strong>Tip:</strong> We won't ask you for your password again for the next hour."
invalid_password: Invalid password
prompt: Confirm password to continue
+ chats:
+ errors:
+ creator_started: Cannot accept this chat since you started it
+ blocked_constraints: Cannot perform request due to blocked constraints
+ unfollowed_and_left_chat_by_user: This user has left the chat and no longer follows you
+ message_creation: An error occurred while creating message
+ channel_reaction: Reactions are not supported for channels
+ marketing_reply: Truth Social cannot receive direct messages. To contact us, email support@truthsocial.com!
crypto:
errors:
invalid_key: is not a valid Ed25519 or Curve25519 key
@@ -900,6 +1004,22 @@
title: This page is not correct
'503': The page could not be served due to a temporary server failure.
noscript_html: To use the Truth web application, please enable JavaScript. Alternatively, try one of the <a href="%{apps_path}">native apps</a> for Truth for your platform.
+ api:
+ '401': This method requires an authenticated user
+ '403': This action is not allowed
+ '404': Record not found
+ '503': There was a temporary problem serving your request, please try again
+ assertion: Unable to verify assertion
+ attestation: Unable to verify attestation
+ data_fetch: Remote data could not be fetched
+ duplicate: Duplicate record
+ login_disabled: Your login is currently disabled
+ login_pending: Your login is currently pending approval
+ sms_reverification_pending: Your account is currently pending SMS verification
+ missing_email: Your login is missing a confirmed e-mail address
+ ssl: Remote SSL certificate could not be verified
+ outside_scopes: This action is outside the authorized scopes
+ unauthorized: Not authorized
existing_username_validator:
not_found: could not find a local user with that username
not_found_multiple: could not find %{usernames}
@@ -923,6 +1043,10 @@
errors:
limit: You have already featured the maximum amount of hashtags
hint_html: "<strong>What are featured hashtags?</strong> They are displayed prominently on your public profile and allow people to browse your public posts specifically under those hashtags. They are a great tool for keeping track of creative works or long-term projects."
+ feeds:
+ errors:
+ feed_creation_limit: Feed Limit Reached. You've reached the maximum number of custom feeds. Delete a feed to add another.
+ too_many_pinned: Feed Pinned Limit Reached. You've reached the maximum number of pinned feeds. Unpin a feed to pin another.
filters:
contexts:
account: Profiles
@@ -957,6 +1081,22 @@
validation_errors:
one: Something isn't quite right yet! Please review the error below
other: Something isn't quite right yet! Please review %{count} errors below
+ groups:
+ errors:
+ unsupported_remote_role: Moderation roles are not supported for remote users yet
+ invalid_name: Please remove invalid characters
+ group_taken: This group name is taken
+ too_many_tags: 'Validation failed: Tags cannot exceed 3'
+ group_creation_limit: You've reached the group creation threshold
+ group_membership_limit: You've reached the group membership threshold
+ invalid_group_tag: 'Validation failed: Tag is invalid'
+ group_deleted: This group no longer exists
+ pending_request_conflict: Group owner or admin has already taken action on this request
+ too_many_admins: You can assign up to %{count} admins for the group at this time.
+ members:
+ one: Member
+ other: Members
+ members_of: Members of group %{name}
html_validator:
invalid_markup: 'contains invalid HTML markup: %{error}'
identity_proofs:
@@ -1023,6 +1163,31 @@
invite_pending: Invite pending
invite_redemed: Invite accepted
title: Invite people
+ links:
+ title:
+ default: "Are you sure you want to leave Truth Social?"
+ blocked: "The Site Ahead Contains Malware"
+ missing: "Page Not Found"
+ warning: "Warning: Potential Security Risk Ahead"
+ summary:
+ default: |
+ This link is taking you to a site outside of Truth Social
+ blocked: |
+ Attackers currently on %{url} might attempt to install dangerous programs on your device
+ that steal or delete your information (for example, photos, passwords, messages and
+ credit cards.)
+ missing: |
+ Sorry, the link you requested does not exist.
+ warning: |
+ Truth Social detected a potential security threat and did not continue to
+ %{url}. If you visit this site, attackers
+ could try to steal information like your passwords, emails, or
+ credit card details.
+ actions:
+ continue: Accept the risk and continue
+ go_back: Back to Truth Social
+ go_back_recommended: Back to Truth Social (recommended)
+ go_to_site: Go to Site
lists:
errors:
limit: You have reached the maximum amount of lists
@@ -1069,6 +1234,10 @@
carry_mutes_over_text: This user moved from %{acct}, which you had muted.
copy_account_note_text: 'This user moved from %{acct}, here were your previous notes about them:'
notification_mailer:
+ chat:
+ subject: "New message from %{name}"
+ subject_android: "sent you a message"
+ sent_message: "Sent you a message"
digest:
action: View all notifications
body: Here is a brief summary of the messages you missed since your last visit on %{since}
@@ -1079,46 +1248,112 @@
subject:
one: "1 new notification since your last visit \U0001F418"
other: "%{count} new notifications since your last visit \U0001F418"
+ subject_android:
+ one: "1 new notification since your last visit \U0001F418"
+ other: "%{count} new notifications since your last visit \U0001F418"
title: In your absence...
favourite:
body: 'Your post was liked by %{name}:'
- subject: "%{name} liked your post"
+ subject: "%{name} liked your Truth"
+ subject_android: "liked your Truth"
title: New favorite
favourite_group:
subject: "%{name} + %{count_others} %{actor} liked your Truth"
+ subject_android: "%{name} and %{count_others} %{actor} liked your Truth"
follow:
body: "%{name} is now following you!"
subject: "%{name} is now following you"
+ subject_android: "is now following you"
title: New follower
+ group_favourite:
+ body: "%{name} liked your Truth in %{group}"
+ subject: "%{name} liked your Truth in %{group}"
+ subject_android: "liked your Truth in %{group}"
+ title: New favorite
+ group_favourite_group:
+ subject: "%{name} + %{count_others} %{actor} liked your Truth in %{group}"
+ subject_android: "%{name} and %{count_others} %{actor} liked your Truth in %{group}"
follow_group:
subject: "%{name} + %{count_others} %{actor} followed you"
+ subject_android: "%{name} and %{count_others} %{actor} followed you"
follow_request:
action: Manage follow requests
body: "%{name} has requested to follow you"
subject: 'Pending follower: %{name}'
+ subject_android: 'requested to follow you'
title: New follow request
mention:
action: Reply
- body: 'You were mentioned by %{name} in:'
+ body: 'You were mentioned by %{name}'
subject: You were mentioned by %{name}
+ subject_android: "mentioned you in a Truth"
title: New mention
mention_group:
subject: "%{name} + %{count_others} %{actor} mentioned you"
+ subject_android: "%{name} and %{count_others} %{actor} mentioned you"
+ group_mention:
+ action: Reply
+ body: 'You were mentioned by %{name} in %{group}'
+ subject: 'You were mentioned by %{name} in %{group}'
+ subject_android: 'mentioned you in %{group}'
+ title: New mention
+ group_mention_group:
+ subject: "%{name} + %{count_others} %{actor} mentioned you"
+ subject_android: "%{name} and %{count_others} %{actor} mentioned you"
poll:
subject: A poll by %{name} has ended
+ subject_android: 'concluded a poll'
reblog:
body: 'Your post was ReTruthed by %{name}:'
- subject: "%{name} ReTruthed your post"
+ subject: "%{name} ReTruthed your Truth"
+ subject_android: "ReTruthed your Truth"
title: New ReTruth
reblog_group:
subject: "%{name} + %{count_others} %{actor} ReTruthed your Truth"
+ subject_android: "%{name} and %{count_others} %{actor} ReTruthed your Truth"
+ group_delete:
+ body: "%{group} has been deleted"
+ subject: "%{group} has been deleted"
+ subject_android: "has been deleted"
+ title: Group Deleted
+ group_approval:
+ body: "%{group} approved your request"
+ subject: "%{group} approved your request"
+ subject_android: "approved your request"
+ title: Accepted to group
+ group_reblog:
+ body: "%{name} ReTruthed your Truth from %{group}"
+ subject: "%{name} ReTruthed your Truth from %{group}"
+ subject_android: "ReTruthed your Truth from %{group}"
+ title: New ReTruth
+ group_reblog_group:
+ subject: "%{name} + %{count_others} %{actor} ReTruthed your group Truth"
+ subject_android: "%{name} and %{count_others} %{actor} ReTruthed your group Truth"
+ group_request:
+ body: "You have a new member request for %{group}"
+ subject: "You have a new member request for %{group}"
+ subject_android: "you have a new member request"
+ title: New member request
+ group_promoted:
+ body: You are now an admin for %{group}
+ subject: You are now an admin for %{group}
+ subject_android: you are now an admin
+ title: You're an admin
+ group_demoted:
+ body: You are no longer an admin for %{group}
+ subject: You are no longer an admin for %{group}
+ subject_android: you are no longer admin
+ title: You're no longer an admin
status:
subject: "%{name} just posted"
+ subject_android: "just posted"
user_approved:
edit_profile_action: Go to profile
edit_profile_step: Lastly, make sure to customize your profile by uploading an avatar, header, changing your display name and more. If youโ€™d like to review new followers before theyโ€™re allowed to follow you, you can lock your account.
- explanation: We are very excited to welcome you to our Truth Seeking community. We believe in free expression and encourage all viewpoints as we do not discriminate against political ideology.
- extra_step: Also, a friendly reminder, we are a new platform, and we are still fixing many bugs in our technology. We promise we are working as hard as we can to make things better as fast as possible! Thanks!
+ explanation: Let us be the first to welcome you to Truth Social! We're thrilled you've joined our growing free speech community and support our goal to keep the Internet open for everyone.
+ extra_step: If you ever have a question about how to use the app, please visit our %{help_center_link} or feel free to email our Support Team at %{mailto_support}. Happy Truthing!
+ signoff: The Truth Social Team
+ help_center: Help Center
final_action: Start posting
final_step: 'Start posting! Even without followers your public posts may be seen by others, for example on the local timeline and in hashtags. You may want to introduce yourself on the #introductions hashtag.'
full_handle: Your full handle
@@ -1126,16 +1361,16 @@
review_preferences_action: Change preferences
review_preferences_step: Make sure to set your preferences, such as which emails you'd like to receive, or what privacy level youโ€™d like your posts to default to. If you donโ€™t have motion sickness, you could choose to enable GIF autoplay.
subject: "Your wait is over! Tap here to start using Truth Social."
- web:
- subject: "Welcome to Truth"
+ subject_android: "Your wait is over! Tap here to start using Truth Social."
tip_federated_timeline: The federated timeline is a firehose view of the Truth network. But it only includes people your neighbours are subscribed to, so it's not complete.
tip_following: You follow your server's admin(s) by default. To find more interesting people, check the local and federated timelines.
tip_local_timeline: The local timeline is a firehose view of people on %{instance}. These are your immediate neighbours!
tip_mobile_webapp: If your mobile browser offers you to add Truth to your homescreen, you can receive push notifications. It acts like a native app in many ways!
tips: Tips
- title: Welcome to Truth Social, %{name}!
+ title: Welcome to Truth Social, @%{name}!
verify_sms_prompt:
subject: Your account is now active on Truth Social! Upgrade to the latest version and join us now!
+ subject_android: Your account is now active on Truth Social! Upgrade to the latest version and join us now!
notifications:
email_events: Events for e-mail notifications
email_events_hint: 'Select events that you want to receive notifications for:'
@@ -1159,6 +1394,7 @@
manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:'
setup: Set up
wrong_code: The entered code was invalid!
+ invalid_code: "The code doesn't match. Please try again."
pagination:
newer: Newer
next: Next
@@ -1303,11 +1539,20 @@
one: 'contained a disallowed hashtag: %{tags}'
other: 'contained the disallowed hashtags: %{tags}'
errors:
- in_reply_not_found: The post you are trying to reply to does not appear to exist.
+ not_permitted_to_post: You cannot post to this group.
+ in_reply_not_found: The Truth you are trying to reply to does not appear to exist.
+ mention_mismatch: Unable to post your Truth at this time.
+ group_errors:
+ invalid_group_id: Invalid group identifier.
+ invalid_membership: Only members may post to this group.
+ invalid_reply: The post being replied to is not in this group.
+ invalid_visibility: You cannot post to this group.
language_detection: Automatically detect language
open_in_web: Open in web
over_character_limit: character limit of %{max} exceeded
pin_errors:
+ group: Group posts cannot be pinned
+ group_ownership: Only group owners can pin posts
limit: You have already pinned the maximum number of posts
ownership: Someone else's post cannot be pinned
private: Non-public posts cannot be pinned
@@ -1328,6 +1573,7 @@
title: '%{name}: "%{quote}"'
visibilities:
direct: Direct
+ group: Group
private: Followers-only
private_long: Only show to followers
public: Public
@@ -1458,12 +1704,12 @@
subject: Please confirm attempted sign in
title: Sign in attempt
status_removed:
- explanation: You have a Truth that has been removed for violating Truth Social community guidelines.
- title: Your Truth has been removed
- subject: Your Truth has been removed
+ explanation: We use artificial intelligence (AI) to assist our hardworking moderators, and some Truths are flagged for deletion or marked โ€œsensitiveโ€ by AI. While the AI we use is very good, it is not error-proof. Assisted by technology, our moderators use their best judgment to ensure compliance with our Terms of Service. Please give our team time to review your Truth to determine whether it violates our Terms of Service. After a thorough review, we will reinstate the Truth or uphold its removal.
+ title: Your Truth was flagged for review
+ subject: Your Truth was flagged for review
warning:
explanation:
- ban_html: After careful review we have decided to delete your account permanently due to Truth social community guidelines violations. If you feel we have made an error, you can file an appeal with %{email}.
+ ban_html: Your account has been banned for violating our Terms of Service. Indefinite bans are a rare and severe sanction, and are generally reserved for the most egregious violations of our Terms of Service. If you feel our decision to ban your account was unjust, immoral, or downright wrong, we encourage you to submit an appeal. Please send an email to %{email}. In the subject line, please write "@Appeal," along with your username. Our team will review your ban appeal at our earliest convenience. After careful review, we will reverse or uphold the ban.
disable_html: You can no longer login to your account or use it in any other way, but your profile and other data remains intact.
sensitive_html: Your uploaded media files and linked media will be treated as sensitive.
silence_html: You can still use your account but only people who are already following you will see your posts on this server, and you may be excluded from various public listings. However, others may still manually follow you.
@@ -1502,6 +1748,8 @@
invalid_otp_token: Invalid two-factor code
invalid_sign_in_token: Invalid security code
otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
+ previously_used_password: Please use a password you have not previously used.
+ password_mismatch: Password and password confirmation do not match.
seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available.
signed_in_as: 'Signed in as:'
suspicious_sign_in_confirmation: You appear to not have logged in from this device before, and you haven't logged in for a while, so we're sending a security code to your e-mail address to confirm that it's you.
@@ -1525,3 +1773,7 @@
not_supported: This browser doesn't support security keys
otp_required: To use security keys please enable two-factor authentication first.
registered_on: Registered on %{date}
+ activerecord:
+ attributes:
+ group:
+ slug: 'Display name'
diff -ru truth-old/opensource/config/locales/es-AR.yml truth-new/opensource/config/locales/es-AR.yml
--- truth-old/opensource/config/locales/es-AR.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/locales/es-AR.yml 2024-04-01 14:59:13
@@ -942,6 +942,9 @@
validation_errors:
one: "ยกFalta algo! Por favor, revisรก el error abajo"
other: "ยกFalta algo! Por favor, revisรก los %{count} errores abajo"
+ groups:
+ errors:
+ too_many_admins: Puede asignar hasta %{count} administradores para el grupo en este momento.
html_validator:
invalid_markup: 'contiene markup HTML no vรกlido: %{error}'
identity_proofs:
@@ -1457,6 +1460,8 @@
invalid_otp_token: Cรณdigo de dos factores no vรกlido
invalid_sign_in_token: Cรณdigo de seguridad no vรกlido
otp_lost_help_html: Si perdiste al acceso a ambos, podรฉs ponerte en contacto con %{email}
+ previously_used_password: Por favor use una contraseรฑa que no haya usado previamente.
+ password_mismatch: La contraseรฑa y la confirmaciรณn de contraseรฑa no son iguales.
seamless_external_login: Iniciaste sesiรณn desde un servicio externo, asรญ que la configuraciรณn de contraseรฑa y correo electrรณnico no estรกn disponibles.
signed_in_as: 'Iniciaste sesiรณn como:'
suspicious_sign_in_confirmation: Parece que no iniciaste sesiรณn desde este dispositivo antes, y no iniciaste sesiรณn durante un tiempo, asรญ que te estamos enviando un cรณdigo de seguridad a tu direcciรณn de correo electrรณnico para confirmar que sos vos.
diff -ru truth-old/opensource/config/locales/es-MX.yml truth-new/opensource/config/locales/es-MX.yml
--- truth-old/opensource/config/locales/es-MX.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/locales/es-MX.yml 2024-04-01 14:59:13
@@ -942,6 +942,9 @@
validation_errors:
one: "ยกAlgo no estรก bien! Por favor, revisa el error"
other: "ยกAlgo no estรก bien! Por favor, revise %{count} errores mรกs abajo"
+ groups:
+ errors:
+ too_many_admins: Puede asignar hasta %{count} administradores para el grupo en este momento.
html_validator:
invalid_markup: 'contiene cรณdigo HTML no vรกlido: %{error}'
identity_proofs:
@@ -1457,6 +1460,8 @@
invalid_otp_token: Cรณdigo de dos factores incorrecto
invalid_sign_in_token: Cรณdigo de seguridad no vรกlido
otp_lost_help_html: Si perdiste al acceso a ambos, puedes ponerte en contancto con %{email}
+ previously_used_password: Por favor use una contraseรฑa que no haya usado previamente.
+ password_mismatch: La contraseรฑa y la confirmaciรณn de contraseรฑa no son iguales.
seamless_external_login: Has iniciado sesiรณn desde un servicio externo, asรญ que los ajustes de contraseรฑa y correo no estรกn disponibles.
signed_in_as: 'Sesiรณn iniciada como:'
suspicious_sign_in_confirmation: Parece que no has iniciado sesiรณn desde este dispositivo antes, y no has iniciado sesiรณn durante un tiempo, asรญ que estamos enviando un cรณdigo de seguridad a tu direcciรณn de correo electrรณnico para confirmar que eres tรบ.
diff -ru truth-old/opensource/config/locales/es.yml truth-new/opensource/config/locales/es.yml
--- truth-old/opensource/config/locales/es.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/locales/es.yml 2024-04-01 14:59:13
@@ -942,6 +942,9 @@
validation_errors:
one: "ยกAlgo no estรก bien! Por favor, revisa el error"
other: "ยกAlgo no estรก bien! Por favor, revise %{count} errores mรกs abajo"
+ groups:
+ errors:
+ too_many_admins: Puede asignar hasta %{count} administradores para el grupo en este momento.
html_validator:
invalid_markup: 'contiene cรณdigo HTML no vรกlido: %{error}'
identity_proofs:
@@ -1457,6 +1460,8 @@
invalid_otp_token: Cรณdigo de dos factores incorrecto
invalid_sign_in_token: Cรณdigo de seguridad no vรกlido
otp_lost_help_html: Si perdiste al acceso a ambos, puedes ponerte en contancto con %{email}
+ previously_used_password: Por favor use una contraseรฑa que no haya usado previamente.
+ password_mismatch: La contraseรฑa y la confirmaciรณn de contraseรฑa no son iguales.
seamless_external_login: Has iniciado sesiรณn desde un servicio externo, asรญ que los ajustes de contraseรฑa y correo no estรกn disponibles.
signed_in_as: 'Sesiรณn iniciada como:'
suspicious_sign_in_confirmation: Parece que no has iniciado sesiรณn desde este dispositivo antes, y no has iniciado sesiรณn durante un tiempo, asรญ que estamos enviando un cรณdigo de seguridad a tu direcciรณn de correo electrรณnico para confirmar que eres tรบ.
diff -ru truth-old/opensource/config/locales/pt-BR.yml truth-new/opensource/config/locales/pt-BR.yml
--- truth-old/opensource/config/locales/pt-BR.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/locales/pt-BR.yml 2024-04-05 09:17:47
@@ -53,6 +53,8 @@
other: usuรกrios
user_count_before: Casa de
what_is_mastodon: O que รฉ Mastodon?
+ ads:
+ why_copy: Mostramos anรบncios de produtos e serviรงos que achamos que nossos usuรกrios podem gostar.
accounts:
choices_html: 'Sugestรตes de %{name}:'
endorsements_hint: Vocรช pode sugerir pessoas que vocรช segue, elas aparecerรฃo aqui.
@@ -641,6 +643,8 @@
edit_preset: Editar o aviso prรฉ-definido
title: Gerenciar os avisos prรฉ-definidos
admin_mailer:
+ account_invitation:
+ subject: Vocรช foi convidado a entrar no Truth Social!
new_pending_account:
body: Os detalhes da nova conta estรฃo abaixo. Vocรช pode aprovar ou vetar.
subject: Nova conta para revisรฃo em %{instance} (%{username})
@@ -698,7 +702,7 @@
didnt_get_confirmation: Nรฃo recebeu instruรงรตes de confirmaรงรฃo?
dont_have_your_security_key: Nรฃo estรก com a sua chave de seguranรงa?
forgot_password: Esqueceu a sua senha?
- invalid_reset_password_token: Cรณdigo de alteraรงรฃo de senha รฉ invรกlido ou expirou. Por favor, solicite um novo.
+ invalid_reset_password_token: O token de redefiniรงรฃo de senha รฉ invรกlido ou expirou. Solicite um novo token.
link_to_otp: Digite um cรณdigo de duas etapas do seu telefone ou um cรณdigo de recuperaรงรฃo
link_to_webauth: Use seu dispositivo de chave de seguranรงa
login: Entrar
@@ -742,6 +746,11 @@
hint_html: "<strong>Dica:</strong> Nรฃo pediremos novamente sua senha pela prรณxima hora."
invalid_password: Senha invรกlida
prompt: Confirme sua senha para continuar
+ chats:
+ errors:
+ creator_started: Nรฃo foi possรญvel aceitar esse chat jรก que vocรช o iniciou
+ blocked_constraints: Nรฃo foi possรญvel executar a solicitaรงรฃo devido a restriรงรตes bloqueadas
+ unfollowed_and_left_chat_by_user: This user has left the chat and no longer follows you
crypto:
errors:
invalid_key: nรฃo รฉ uma chave Ed25519 ou Curve25519 vรกlida
@@ -762,11 +771,11 @@
x_months: "%{count}m"
x_seconds: "%{count}seg"
deletes:
- challenge_not_passed: As informaรงรตes que vocรช inseriu nรฃo estรฃo corretas
+ challenge_not_passed: A informaรงรฃo que vocรช inseriu nรฃo estรก correta
confirm_password: Digite a sua senha atual para verificar a sua identidade
confirm_username: Digite seu nome de usuรกrio para confirmar o procedimento
proceed: Excluir conta
- success_msg: A sua conta foi excluรญda com sucesso
+ success_msg: Sua conta foi excluรญda com sucesso
warning:
before: 'Antes de prosseguir, por favor leia com cuidado:'
caches: Conteรบdo que foi armazenado em cache por outras instรขncias pode continuar a existir
@@ -793,12 +802,27 @@
'422':
content: Falha na verificaรงรฃo de seguranรงa. Vocรช estรก bloqueando cookies?
title: Falha na verificaรงรฃo de seguranรงa
- '429': Muitas solicitaรงรตes
+ '429': Excesso de solicitaรงรตes
'500':
content: Desculpe, algo deu errado por aqui.
title: Esta pรกgina nรฃo estรก certa
'503': A pรกgina nรฃo pรดde ser carregada devido a uma falha temporรกria do servidor.
noscript_html: Para usar o aplicativo web do Mastodon, por favor ative o JavaScript. Ou, se quiser, experimente um dos <a href="%{apps_path}">aplicativos nativos</a> para o Mastodon em sua plataforma.
+ api:
+ '401': Esse mรฉtodo requer um usuรกrio autenticado
+ '403': Essa aรงรฃo nรฃo รฉ permitida
+ '404': Registro nรฃo encontrado
+ '503': Ocorreu um problema temporรกrio ao atender sua solicitaรงรฃo. Tente novamente.
+ assertion: Nรฃo foi possรญvel verificar sua afirmaรงรฃo
+ attestation: Nรฃo foi possรญvel verificar seu atestado
+ data_fetch: Nรฃo foi possรญvel resgatar os dados remotos
+ duplicate: Registro duplicado
+ login_disabled: No momento seu login estรก desativado
+ login_pending: No momento seu login estรก aguardando aprovaรงรฃo
+ missing_email: Estรก faltando um endereรงo de e-mail confirmado para o seu login
+ ssl: Nรฃo foi possรญvel verificar o Certificado SSL remoto
+ outside_scopes: Essa aรงรฃo estรก fora do escopo autorizado
+ unauthorized: Nรฃo autorizado
existing_username_validator:
not_found: nรฃo foi possรญvel encontrar um usuรกrio local com esse nome de usuรกrio
not_found_multiple: nรฃo foi possรญvel encontrar %{usernames}
@@ -846,7 +870,7 @@
trending_now: Em alta no momento
generic:
all: Tudo
- changes_saved_msg: Alteraรงรตes foram salvas com sucesso!
+ changes_saved_msg: Alteraรงรตes salvas com sucesso!
copy: Copiar
delete: Excluir
no_batch_actions_available: Nenhuma aรงรฃo em lote disponรญvel nesta pรกgina
@@ -855,6 +879,10 @@
validation_errors:
one: Algo errado nรฃo estรก certo! Por favor, analise o erro abaixo
other: Algo errado nรฃo estรก certo! Por favor, analise os %{count} erros abaixo
+ groups:
+ errors:
+ pending_request_conflict: O proprietรกrio ou administrador do grupo jรก tomou medidas sobre esta solicitaรงรฃo.
+ too_many_admins: Vocรช pode atribuir atรฉ %{count} administradores para o grupo.
html_validator:
invalid_markup: 'contรฉm HTML invรกlido: %{error}'
identity_proofs:
@@ -920,9 +948,9 @@
limit: Vocรช atingiu o mรกximo de listas
media_attachments:
validations:
- images_and_video: Nรฃo foi possรญvel anexar um vรญdeo a um toot que jรก contรฉm imagens
- not_ready: Nรฃo รฉ possรญvel anexar arquivos que nรฃo terminaram de ser processados. Tente novamente daqui a pouco!
- too_many: Nรฃo foi possรญvel anexar mais de 4 imagens
+ images_and_video: Nรฃo รฉ possรญvel anexar um vรญdeo a uma postagem que jรก contรฉm imagens
+ not_ready: Nรฃo รฉ possรญvel anexar arquivos cujo processamento ainda nรฃo foi concluรญdo. Tente novamente daqui a pouco!
+ too_many: Nรฃo รฉ possรญvel anexar mais de 4 arquivos
migrations:
acct: Mudou-se para
cancel: Cancelar redirecionamento
@@ -961,39 +989,133 @@
carry_mutes_over_text: Este usuรกrio mudou de %{acct}, que vocรช havia silenciado.
copy_account_note_text: 'Este usuรกrio saiu de %{acct}, aqui estรฃo suas notas anteriores sobre ele:'
notification_mailer:
+ chat:
+ subject: "Nova mensagem de %{name}"
+ subject_android: "lhe enviou uma mensagem"
+ sent_message: "Nova mensagem"
digest:
action: Ver todas as notificaรงรตes
- body: Aqui estรก um breve resumo das mensagens que vocรช perdeu desde o seu รบltimo acesso em %{since}
- mention: "%{name} te mencionou em:"
+ body: Aqui estรก um breve resumo das mensagens que vocรช nรฃo viu desde seu รบltimo acesso em %{since}
+ mention: "%{name} mencionou vocรช em:"
new_followers_summary:
- one: Vocรช tem um novo seguidor! Uia!
- other: Vocรช tem %{count} novos seguidores! AรŠรŠรŠ!
+ one: Alรฉm disso, enquanto estava fora vocรช ganhou um novo seguidor! Oba!
+ other: Alรฉm disso, enquanto estava fora vocรช ganhou %{count} novos seguidores! Incrรญvel!
subject:
- one: "Uma nova notificaรงรฃo desde o seu รบltimo acesso \U0001F418"
- other: "%{count} novas notificaรงรตes desde o seu รบltimo acesso \U0001F418"
- title: Enquanto vocรช estava ausente...
+ one: "1 nova notificaรงรฃo desde seu รบltimo acesso \U0001F418"
+ other: "%{count} novas notificaรงรตes desde seu รบltimo acesso \U0001F418"
+ subject_android:
+ one: "1 nova notificaรงรฃo desde seu รบltimo acesso \U0001F418"
+ other: "%{count} novas notificaรงรตes desde seu รบltimo acesso \U0001F418"
+ title: Na sua ausรชncia...
favourite:
- body: "%{name} favoritou seu toot:"
- subject: "%{name} favoritou seu toot"
+ body: "Sua postagem foi curtida por %{name}:"
+ subject: "%{name} curtiu sua postagem"
+ subject_android: "gostou da sua Truth"
title: Novo favorito
+ favourite_group:
+ subject: "%{name} + %{count_others} %{actor} curtiram sua Truth"
+ subject_android: "%{name} e mais %{count_others} pessoas gostaram da sua Truth"
+ group_favourite:
+ body: "Sua postagem foi curtida por %{name}:"
+ subject: "%{name} curtiu sua postagem"
+ subject_android: "gostou da sua Truth em %{group}"
+ title: Novo favorito
+ group_favourite_group:
+ subject: "%{name} + %{count_others} %{actor} curtiram sua Truth"
+ subject_android: "%{name} e mais %{count_others} pessoas gostaram da sua Truth em %{group}"
follow:
- body: "%{name} te seguiu!"
- subject: "%{name} te seguiu"
+ body: "%{name} estรก seguindo vocรช!"
+ subject: "%{name} estรก seguindo vocรช"
+ subject_android: "estรก te seguindo agora"
title: Novo seguidor
+ follow_group:
+ subject: "%{name} + %{count_others} %{actor} seguiram vocรช"
+ subject_android: "%{name} e %{count_others} outros seguiram vocรช"
follow_request:
- action: Gerenciar seguidores pendentes
- body: "%{name} quer te seguir"
- subject: 'Seguidor pendente: %{name}'
- title: Novo seguidor pendente
+ action: Gerenciar solicitaรงรตes de seguidores
+ body: "%{name} pediu para seguir vocรช"
+ subject: 'Seguidor aguardando: %{name}'
+ subject_android: "pediu para te seguir"
+ title: Nova solicitaรงรฃo de seguidor
mention:
action: Responder
- body: "%{name} te mencionou em:"
- subject: "%{name} te mencionou"
+ body: "Vocรช foi mencionado por %{name} em:"
+ subject: "Vocรช foi mencionado por %{name}"
+ subject_android: "mencionou vocรช em uma Truth"
title: Nova menรงรฃo
+ mention_group:
+ subject: "%{name} + %{count_others} %{actor} mencionaram vocรช"
+ subject_android: "%{name} e %{count_others} outros mencionaram vocรช"
+ group_mention:
+ action: Responder
+ body: "Vocรช foi mencionado por %{name} em:"
+ subject: "Vocรช foi mencionado por %{name}"
+ subject_android: "mencionou vocรช em %{group}"
+ title: Nova menรงรฃo
+ group_mention_group:
+ subject: "%{name} + %{count_others} %{actor} mencionaram vocรช"
+ subject_android: "%{name} e %{count_others} outros mencionaram vocรช"
+ poll:
+ subject: Uma enquete postada por %{name} foi encerrada
+ subject_android: "concluiu uma enquete"
reblog:
- body: "%{name} deu boost no seu toot:"
- subject: "%{name} deu boost no seu toot"
- title: Novo boost
+ body: "Sua Truth foi RePostada por %{name}:"
+ subject: "%{name} RePostou sua Truth"
+ subject_android: "ReTruthed sua Truth"
+ title: Nova RePostagem
+ reblog_group:
+ subject: "%{name} + %{count_others} %{actor} RePostaram sua Truth"
+ subject_android: "%{name} e mais %{count_others} outros ReTruthed sua Truth"
+ group_reblog:
+ body: "Sua Truth foi RePostada por %{name}:"
+ subject: "%{name} RePostou sua Truth"
+ subject_android: "ReTruthed sua Truth de %{group}"
+ title: Nova RePostagem
+ group_reblog_group:
+ subject: "%{name} + %{count_others} %{actor} RePostaram sua Truth"
+ subject_android: "%{name} e %{count_others} outros ReTruthed a sua Truth de grupo"
+ group_request:
+ body: '%{name} wants to join your group:'
+ subject: "%{name} wants to join your group"
+ subject_android: "vocรช tem uma nova solicitaรงรฃo de membro"
+ title: Group Join Request
+ status:
+ subject: "%{name} acabou de postar"
+ subject_android: "acabou de postar"
+ group_promoted:
+ body: You are now an admin for %{group}
+ subject: You are now an admin for %{group}
+ subject_android: "agora vocรช รฉ um administrador"
+ title: You're an admin
+ group_demoted:
+ body: You are no longer an admin for %{group}
+ subject: You are no longer an admin for %{group}
+ subject_android: "vocรช nรฃo รฉ mais administrador"
+ title: You're no longer an admin
+ user_approved:
+ edit_profile_action: Ir para o perfil
+ edit_profile_step: Para terminar, nรฃo deixe de personalizar seu perfil carregando um avatar, um tรญtulo, mudando seu nome escolhido e muito mais. Se quiser avaliar novos seguidores antes de aprovรก-los, vocรช pode bloquear sua conta.
+ explanation: Estamos super empolgados com sua chegada ร  nossa comunidade de Buscadores da Verdade. Acreditamos na liberdade de expressรฃo e incentivamos todos os pontos de vista, jรก que nรฃo discriminamos ninguรฉm devido a uma ideologia polรญtica.
+ extra_step: Alรฉm disso, gostarรญamos de lembrar que somos uma plataforma nova e ainda estamos corrigindo vรกrios bugs na nossa tecnologia. Podemos garantir que estamos trabalhando duro para aprimorar tudo o mais rรกpido possรญvel! Obrigado!
+ final_action: Comece a postar
+ final_step: 'Comece a postar! Mesmo sem nenhum seguidor suas postagens pรบblicas podem ser vistas por outras pessoas, por exemplo, na timeline local e nas hashtags. Vocรช pode querer se apresentar com a hashtag #apresentaรงรตes.'
+ full_handle: Seu identificador completo (handle)
+ full_handle_hint: ร‰ o que vocรช quer dizer aos seus amigos para que possam seguir vocรช ou lhe enviar mensagens de outro servidor.
+ review_preferences_action: Mudar preferรชncias
+ review_preferences_step: Nรฃo deixe de configurar suas preferรชncias, como os e-mails que quer receber ou o nรญvel de privacidade padrรฃo para as suas postagens. Se nรฃo costuma sentir enjoo vocรช pode optar por reproduzir GIFs automaticamente.
+ subject: "Sua espera terminou! Toque aqui para comeรงar a usar o Truth Social."
+ subject_android: "Sua espera terminou! Toque aqui para comeรงar a usar o Truth Social."
+ web:
+ subject: "Bem-vindo(a) ao Truth, onde a Verdade impera"
+ tip_federated_timeline: A timeline federada รฉ uma ampla visรฃo ao vivo da rede do Truth. Mas sรณ inclui as pessoas que seus vizinhos estรฃo seguindo, entรฃo nรฃo รฉ uma visรฃo completa.
+ tip_following: Vocรช segue os administradores do seu servidor por padrรฃo Para encontrar mais gente interessante, verifique sua timeline local e federada.
+ tip_local_timeline: A timeline local รฉ uma visรฃo ao vivo das pessoas em %{instance}. Sรฃo seus vizinhos de porta!
+ tip_mobile_webapp: Se o seu navegador mรณvel sugerir que vocรช adicione o Truth ร  sua tela inicial, vocรช poderรก receber notificaรงรตes por push. O Truth funciona de muitas maneiras como um aplicativo nativo!
+ tips: Dicas
+ title: Bem-vindo(a) ao Truth Social, %{name}!
+ verify_sms_prompt:
+ subject: Sua conta jรก estรก ativa no Truth Social! Atualize para a versรฃo mais recente e entre para a nossa turma!
+ subject_android: Sua conta jรก estรก ativa no Truth Social! Atualize para a versรฃo mais recente e entre para a nossa turma!
notifications:
email_events: Eventos para notificaรงรตes por e-mail
email_events_hint: 'Selecione os eventos que deseja receber notificaรงรตes:'
@@ -1014,7 +1136,7 @@
instructions_html: "<strong>Escaneie este cรณdigo QR no Google Authenticator ou em um aplicativo TOTP similar no seu telefone</strong>. A partir de agora, esse aplicativo irรก gerar tokens que vocรช terรก que digitar ao fazer login."
manual_instructions: 'Se vocรช nรฃo pode escanear o cรณdigo QR e precisa digitรก-lo manualmente, aqui estรก o segredo em texto:'
setup: Configurar
- wrong_code: O cรณdigo digitado รฉ invรกlido! O horรกrio do servidor e o horรกrio do dispositivo estรฃo corretos?
+ wrong_code: O cรณdigo que vocรช inseriu รฉ invรกlido!
pagination:
newer: Mais novo
next: Prรณximo
@@ -1108,7 +1230,7 @@
windows_mobile: Windows Mobile
windows_phone: Windows Phone
revoke: Fechar
- revoke_success: Sessรฃo fechada com sucesso
+ revoke_success: A sessรฃo foi revogada com sucesso
title: Sessรตes
settings:
account: Conta
@@ -1150,7 +1272,7 @@
one: 'continha hashtag nรฃo permitida: %{tags}'
other: 'continha hashtags nรฃo permitidas: %{tags}'
errors:
- in_reply_not_found: O toot que vocรช quer responder parece nรฃo existir.
+ in_reply_not_found: Parece que a postagem ร  qual vocรช estรก tentando responder nรฃo existe.
language_detection: Detectar idioma automaticamente
open_in_web: Abrir no navegador
over_character_limit: limite de caracteres de %{max} excedido
@@ -1279,7 +1401,7 @@
two_factor_authentication:
add: Adicionar
disable: Desativar
- disabled_success: Autenticaรงรฃo de dois fatores desabilitada com sucesso
+ disabled_success: Autenticaรงรฃo de dois fatores desativada com sucesso
edit: Editar
enabled: Autenticaรงรฃo de dois fatores ativada
enabled_success: Autenticaรงรฃo de dois fatores ativada com sucesso
@@ -1288,28 +1410,34 @@
methods: Mรฉtodos de dois fatores
otp: Aplicativo autenticador
recovery_codes: Cรณdigos de recuperaรงรฃo de reserva
- recovery_codes_regenerated: Cรณdigos de recuperaรงรฃo gerados com sucesso
+ recovery_codes_regenerated: Cรณdigos de recuperaรงรฃo regerados com sucesso
recovery_instructions_html: Se vocรช perder acesso ao seu celular, vocรช pode usar um dos cรณdigos de recuperaรงรฃo abaixo para acessar a sua conta. <strong>Mantenha os cรณdigos de recuperaรงรฃo em um local seguro</strong>. Por exemplo, vocรช pode imprimi-los e guardรก-los junto com outros documentos importantes.
webauthn: Chaves de seguranรงa
user_mailer:
backup_ready:
explanation: Vocรช pediu um backup completo da sua conta no Mastodon. E agora estรก pronto para ser baixado!
- subject: Seu arquivo estรก pronto para ser baixado
+ subject: Seu arquivo estรก pronto para download
title: Baixar arquivo
sign_in_token:
details: 'Aqui estรฃo os detalhes da tentativa:'
explanation: 'Detectamos uma tentativa de acessar sua conta a partir de um endereรงo IP nรฃo reconhecido. Se for vocรช, insira o cรณdigo de seguranรงa abaixo na pรกgina de desafio:'
further_actions: 'Se nรฃo foi vocรช, por favor mude sua senha e ative a autenticaรงรฃo de dois fatores em sua conta. Vocรช pode fazรช-lo aqui:'
- subject: Por favor, confirme a tentativa de acesso
+ subject: Confirme a tentativa de login
title: Tentativa de acesso
+ status_removed:
+ explanation: We use artificial intelligence (AI) to assist our hardworking moderators, and some Truths are flagged for deletion or marked โ€œsensitiveโ€ by AI. While the AI we use is very good, it is not error-proof. Assisted by technology, our moderators use their best judgment to ensure compliance with our Terms of Service. Please give our team time to review your Truth to determine whether it violates our Terms of Service. After a thorough review, we will reinstate the Truth or uphold its removal.
+ title: Your Truth was flagged for review
+ subject: Sua Truth foi sinalizada para revisรฃo
warning:
explanation:
- disable: Enquanto sua conta estรก congelada, seus dados de conta permanecem intactos, mas vocรช nรฃo pode realizar nenhuma aรงรฃo atรฉ que esteja destrancada.
- sensitive: Seus arquivos de mรญdia carregados e mรญdias vinculadas serรฃo tratados como sensรญveis.
- silence: Enquanto sua conta estรก silenciada, somente pessoas que jรก estรฃo seguindo vocรช poderรฃo ver seus toots nessa instรขncia, e vocรช pode ser excluรญdo de vรกrias listas pรบblicas. No entanto, outros ainda podem te seguir manualmente.
- suspend: Sua conta foi banida e todos os seus toots e mรญdias foram irreversivelmente excluรญdos desta instรขncia e das instรขncias dos seus seguidores.
- verify: Vocรช รฉ um membro certificado da comunidade
- unverify: Vocรช nรฃo รฉ mais um membro certificado da comunidade.
+ ban_html: Sua conta foi banida por violar nossos Termos de Serviรงo. Banimentos sem prazo definido sรฃo um tipo de sanรงรฃo raro e muito grave, geralmente aplicados somente ร s mais sรฉrias violaรงรตes de nossos Termos de Serviรงo. Se vocรช acredita que nossa decisรฃo de banir sua conta foi injusta, imoral ou simplesmente errada, recomendamos que entre com um recurso. Envie um e-mail para %{email}. Na linha de assunto, escreva "@Appeal" junto com seu nome de usuรกrio. Aguarde 48 horas para que nossa equipe avalie seu recurso contra o banimento. Apรณs uma avaliaรงรฃo cuidadosa, decidiremos reverter ou manter o banimento.
+ disable_html: Vocรช nรฃo pode mais entrar na sua conta nem usรก-la de qualquer forma que seja, mas seu perfil e seus outros dados permanecerรฃo intactos.
+ sensitive_html: Os arquivos de mรญdia que vocรช carregou e os links de mรญdia que publicou serรฃo tratados como confidenciais.
+ silence_html: Vocรช continua podendo usar sua conta, mas apenas as pessoas que jรก seguem vocรช verรฃo suas postagens nesse servidor. Alรฉm disso, vocรช poderรก ser excluรญdo de vรกrias listagens pรบblicas. No entanto, as pessoas continuam podendo seguir vocรช manualmente.
+ suspend_html: Sua conta foi suspensa e ficarรก inacessรญvel por %{suspension_duration}.
+ suspend_indefinite_html: Sua conta foi suspensa e ficarรก inacessรญvel.
+ verify_html: Vocรช รฉ um membro certificado da comunidade
+ unverify_html: Vocรช nรฃo รฉ mais um membro certificado da comunidade
get_in_touch: Vocรช pode responder a este e-mail para entrar em contato com a equipe de %{instance}.
review_server_policies: Revisar as polรญticas da instรขncia
statuses: 'Especificamente, para:'
@@ -1329,6 +1457,8 @@
suspend: Conta banida
verify: Conta verificada
unverify: Verificaรงรฃo de conta removida
+ waitlisted:
+ title: Sua conta foi criada com sucesso!
welcome:
edit_profile_action: Configurar perfil
edit_profile_step: Vocรช pode personalizar o seu perfil enviando um avatar, uma capa, alterando seu nome de exibiรงรฃo e etc. Se vocรช preferir aprovar seus novos seguidores antes de eles te seguirem, vocรช pode trancar a sua conta.
@@ -1350,8 +1480,10 @@
follow_limit_reached: Vocรช nรฃo pode seguir mais de %{limit} pessoas
generic_access_help_html: Problemas para acessar sua conta? Vocรช pode entrar em contato com %{email} para obter ajuda
invalid_otp_token: Cรณdigo de dois fatores invรกlido
- invalid_sign_in_token: Cรณgido de seguranรงa invรกlido
+ invalid_sign_in_token: Cรณdigo de seguranรงa invรกlido
otp_lost_help_html: Se vocรช perder o acesso ร  ambos, vocรช pode entrar em contato com %{email}
+ previously_used_password: Por favor, use uma senha que vocรช nรฃo usou anteriormente.
+ password_mismatch: A senha e a confirmaรงรฃo da senha nรฃo coincidem.
seamless_external_login: Vocรช entrou usando um serviรงo externo, entรฃo configuraรงรตes de e-mail e senha nรฃo estรฃo disponรญveis.
signed_in_as: 'Entrou como:'
suspicious_sign_in_confirmation: Parece que vocรช nรฃo fez login deste dispositivo antes, e vocรช nรฃo fez login por um tempo. Portanto, estamos enviando um cรณdigo de seguranรงa para o seu endereรงo de e-mail para confirmar que รฉ vocรช.
diff -ru truth-old/opensource/config/locales/pt-PT.yml truth-new/opensource/config/locales/pt-PT.yml
--- truth-old/opensource/config/locales/pt-PT.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/locales/pt-PT.yml 2024-04-05 09:17:47
@@ -1,87 +1,89 @@
---
-pt-PT:
+pt-BR:
about:
- about_hashtag_html: Estes sรฃo toots pรบblicos marcados com <strong>#%{hashtag}</strong>. Podes interagir com eles se tiveres uma conta Mastodon.
- about_mastodon_html: Mastodon รฉ uma rede social baseada em protocolos abertos da web e software livre e gratuito. ร‰ descentralizado como e-mail.
- about_this: Sobre esta instรขncia
- active_count_after: activo
- active_footnote: Utilizadores activos mensais (UAM)
+ about_hashtag_html: Estes sรฃo toots pรบblicos com a hashtag <strong>#%{hashtag}</strong>. Vocรช pode interagir com eles se tiver uma conta em qualquer lugar no fediverso.
+ about_mastodon_html: 'A rede social do futuro: Sem anรบncios, sem vigilรขncia corporativa, com design รฉtico e muita descentralizaรงรฃo! Possua seus prรณprios dados com o Mastodon!'
+ about_this: Sobre
+ active_count_after: ativo
+ active_footnote: Usuรกrios Ativos Mensalmente (UAM)
administered_by: 'Administrado por:'
api: API
- apps: Aplicaรงรตes mรณveis
- apps_platforms: Usar o Mastodon a partir do iOS, Android e outras plataformas
- browse_directory: Navegue pelo directรณrio de perfis e filtre por interesses
- browse_local_posts: Visualize as publicaรงรตes pรบblicas desta instรขncia em tempo real
- browse_public_posts: Visualize as publicaรงรตes pรบblicas do Mastodon em tempo real
- contact: Contacto
- contact_missing: Nรฃo configurado
- contact_unavailable: n.d.
- discover_users: Descobrir utilizadores
+ apps: Aplicativos
+ apps_platforms: Use o Mastodon a partir do iOS, Android e outras plataformas
+ browse_directory: Navegue pelo diretรณrio de perfis e filtre por interesses
+ browse_local_posts: Navegue pelos toots pรบblicos locais em tempo real
+ browse_public_posts: Navegue pelos toots pรบblicos globais em tempo real
+ contact: Contato
+ contact_missing: Nรฃo definido
+ contact_unavailable: Nรฃo disponรญvel
+ discover_users: Descubra usuรกrios
documentation: Documentaรงรฃo
- federation_hint_html: Ter uma conta em %{instance} permitirรก seguir pessoas em qualquer instรขncia Mastodon.
- get_apps: Experimente uma aplicaรงรฃo
- hosted_on: Mastodon em %{domain}
+ federation_hint_html: Com uma conta em %{instance} vocรช vai poder seguir e interagir com pessoas de qualquer canto do fediverso.
+ get_apps: Experimente um aplicativo
+ hosted_on: Instรขncia Mastodon em %{domain}
instance_actor_flash: |
- Esta conta รฉ um actor virtual usado para representar a prรณpria instรขncia e nรฃo um utilizador individual.
- ร‰ usada para motivos de federaรงรฃo e nรฃo deve ser bloqueada a nรฃo ser que que queira bloquear a instรขncia por completo. Se for esse o caso, deverรก usar o bloqueio de domรญnio.
- learn_more: Saber mais
- privacy_policy: Polรญtica de privacidade
- rules: Regras da instรขncia
- rules_html: 'Abaixo estรก um resumo das regras que precisa seguir se pretender ter uma conta nesta instรขncia do Mastodon:'
- see_whats_happening: Veja o que estรก a acontecer
+ Esta conta รฉ um ator virtual usado para representar o prรณprio servidor e nรฃo qualquer usuรกrio individual.
+ ร‰ usado para propรณsitos de federaรงรฃo e nรฃo deve ser bloqueado a menos que queira bloquear toda a instรขncia, o que no caso devia usar um bloqueio de domรญnio.
+ learn_more: Saiba mais
+ privacy_policy: Polรญtica de Privacidade
+ rules: Regras do servidor
+ rules_html: 'Abaixo estรก um resumo das regras que vocรช precisa seguir se vocรช quer ter uma conta neste servidor do Mastodon:'
+ see_whats_happening: Veja o que estรก acontecendo
server_stats: 'Estatรญsticas da instรขncia:'
- source_code: Cรณdigo fonte
+ source_code: Cรณdigo-fonte
status_count_after:
- one: publicaรงรฃo
- other: publicaรงรตes
- status_count_before: Que fizeram
- tagline: Siga os seus amigos e descubra novas amizades
+ one: toot
+ other: toots
+ status_count_before: Autores de
+ tagline: Siga seus amigos e faรงa novas amizades
terms: Termos de serviรงo
unavailable_content: Conteรบdo indisponรญvel
unavailable_content_description:
domain: Instรขncia
- reason: Motivo
- rejecting_media: 'Arquivos de media destas instรขncias nรฃo serรฃo processados ou armazenados, e nenhuma miniatura serรก exibida, o que requer que o utilizador clique e abra o arquivo original manualmente:'
- rejecting_media_title: Media filtrada
- silenced: 'Publicaรงรตes destas instรขncias serรฃo ocultas em linhas do tempo e conversas pรบblicas, e nenhuma notificaรงรฃo serรก gerada a partir das interaรงรตes dos seus utilizadores, a menos que vocรช os esteja a seguir:'
+ reason: 'Motivo:'
+ rejecting_media: 'Arquivos de mรญdia destas instรขncias nรฃo serรฃo processados ou armazenados e nenhuma miniatura serรก exibida, exigindo que o usuรกrio abra o arquivo original manualmente:'
+ rejecting_media_title: Mรญdia filtrada
+ silenced: 'Toots destas instรขncias serรฃo ocultos em linhas e conversas pรบblicas, e nenhuma notificaรงรฃo serรก gerada a partir das interaรงรตes dos seus usuรกrios, a menos que esteja sendo seguido:'
silenced_title: Servidores silenciados
- suspended: 'Nenhum dado dessas instรขncias serรก processado, armazenado ou trocado, tornando qualquer interaรงรฃo ou comunicaรงรฃo com os utilizadores dessas instรขncias impossรญvel:'
- suspended_title: Servidores suspensos
- unavailable_content_html: Mastodon geralmente permite que vocรช veja o conteรบdo e interaja com utilizadores de qualquer outra instรขncia no fediverso. Estas sรฃo as exceรงรตes desta instรขncia em especรญfico.
+ suspended: 'Vocรช nรฃo serรก capaz de seguir ninguรฉm destas instรขncias, e nenhum dado delas serรก processado, armazenado ou trocado:'
+ suspended_title: Servidores banidos
+ unavailable_content_html: Mastodon geralmente permite que vocรช veja o conteรบdo e interaja com usuรกrios de qualquer outra instรขncia no fediverso. Estas sรฃo as exceรงรตes desta instรขncia em especรญfico.
user_count_after:
- one: utilizador
- other: utilizadores
- user_count_before: Casa para
- what_is_mastodon: O que รฉ o Mastodon?
+ one: usuรกrio
+ other: usuรกrios
+ user_count_before: Casa de
+ what_is_mastodon: O que รฉ Mastodon?
+ ads:
+ why_copy: Mostramos anรบncios de produtos e serviรงos que achamos que nossos usuรกrios podem gostar.
accounts:
- choices_html: 'escolhas de %{name}:'
- endorsements_hint: Vocรช pode, atravรฉs da interface web, escolher endossar pessoas que segue, e elas aparecerรฃo aqui.
- featured_tags_hint: Vocรช pode destacar hashtags especรญficas que serรฃo exibidas aqui.
+ choices_html: 'Sugestรตes de %{name}:'
+ endorsements_hint: Vocรช pode sugerir pessoas que vocรช segue, elas aparecerรฃo aqui.
+ featured_tags_hint: Vocรช pode destacar hashtags especรญficas, elas aparecerรฃo aqui.
follow: Seguir
followers:
one: Seguidor
other: Seguidores
- following: A seguir
- instance_actor_flash: Esta conta รฉ um actor virtual usado para representar a prรณpria instรขncia e nรฃo um utilizador individual. ร‰ usada para motivos de federaรงรฃo e nรฃo deve ser suspenso.
- joined: Aderiu %{date}
- last_active: รบltima vez activo
- link_verified_on: A posse deste link foi verificada em %{date}
- media: Media
- moved_html: "%{name} mudou-se para %{new_profile_link}:"
- network_hidden: Esta informaรงรฃo nรฃo estรก disponรญvel
+ following: Seguindo
+ instance_actor_flash: Esta conta รฉ um ator virtual usado para representar o prรณprio servidor e nรฃo um usuรกrio individual. ร‰ utilizada para fins de federaรงรฃo e nรฃo deve ser suspensa.
+ joined: Entrou em %{date}
+ last_active: รบltima atividade
+ link_verified_on: O link foi verificado em %{date}
+ media: Mรญdia
+ moved_html: "%{name} se mudou para %{new_profile_link}:"
+ network_hidden: Informaรงรฃo indisponรญvel
never_active: Nunca
- nothing_here: Nรฃo hรก nada aqui!
- people_followed_by: Pessoas seguidas por %{name}
+ nothing_here: Nada aqui!
+ people_followed_by: Pessoas que %{name} segue
people_who_follow: Pessoas que seguem %{name}
pin_errors:
- following: Tu tens de estar a seguir a pessoa que pretendes apoiar
+ following: Vocรช deve estar seguindo a pessoa que vocรช deseja sugerir
posts:
- one: Publicaรงรฃo
- other: Publicaรงรตes
- posts_tab_heading: Publicaรงรตes
- posts_with_replies: Posts e Respostas
+ one: Toot
+ other: Toots
+ posts_tab_heading: Toots
+ posts_with_replies: Toots e respostas
roles:
- admin: Administrador(a)
+ admin: Admin
bot: Robรด
group: Grupo
moderator: Moderador
@@ -89,235 +91,193 @@
unfollow: Deixar de seguir
admin:
account_actions:
- action: Executar acรงรฃo
- title: Executar acรงรฃo de moderaรงรฃo em %{acct}
+ action: Tomar uma atitude
+ title: Moderar %{acct}
account_moderation_notes:
- create: Criar nota
+ create: Deixar nota
created_msg: Nota de moderaรงรฃo criada com sucesso!
- delete: Eliminar
+ delete: Excluir
destroyed_msg: Nota de moderaรงรฃo excluรญda com sucesso!
accounts:
- add_email_domain_block: Adicionar o domรญnio de email ร  lista negra
+ add_email_domain_block: Adicionar o domรญnio de e-mail ร  lista negra
approve: Aprovar
- approve_all: Aprovar todos
- approved_msg: Inscriรงรฃo de %{username} aprovada com sucesso
- are_you_sure: Tens a certeza?
- avatar: Imagem de Perfil
+ approve_all: Aprovar tudo
+ approved_msg: Aprovado com sucesso o pedido de registro de %{username}
+ are_you_sure: Vocรช tem certeza?
+ avatar: Imagem de perfil
by_domain: Domรญnio
change_email:
changed_msg: E-mail da conta alterado com sucesso!
- current_email: E-mail actual
+ current_email: E-mail atual
label: Alterar e-mail
new_email: Novo e-mail
submit: Alterar e-mail
title: Alterar e-mail para %{username}
confirm: Confirmar
confirmed: Confirmado
- confirming: A confirmar
- delete: Eliminar dados
- deleted: Eliminada
- demote: Despromoveu
- destroyed_msg: Os dados de %{username} estรฃo agora em fila de espera para serem eliminados de imediato
- disable: Desativar
- disable_two_factor_authentication: Desativar 2FA
- disabled: Congelada
- display_name: Nome a mostrar
+ confirming: Confirmando
+ delete: Excluir dados
+ deleted: Excluรญdo
+ demote: Rebaixar
+ destroyed_msg: Os dados de %{username} estรฃo na fila para serem excluรญdos em breve
+ disable: Congelar
+ disable_two_factor_authentication: Desativar autenticaรงรฃo de dois fatores
+ disabled: Desativada
+ display_name: Nome de exibiรงรฃo
location: localizaรงรฃo
website: local na rede Internet
domain: Domรญnio
edit: Editar
email: E-mail
- email_status: Estado do e-mail
- enable: Ativar
- enabled: Ativado
- enabled_msg: Descongelou com sucesso a conta %{username}
+ email_status: Status do e-mail
+ enable: Descongelar
+ enabled: Ativada
+ enabled_msg: Descongelada com sucesso a conta de %{username}
followers: Seguidores
- follows: A seguir
- header: Cabeรงalho
+ follows: Seguindo
+ header: Capa
inbox_url: URL da caixa de entrada
- invite_request_text: Razรตes para se juntar a nรณs
- invited_by: Convidado(a) por
+ invite_request_text: Motivos para entrar
+ invited_by: Convidado por
ip: IP
- joined: Aderiu
+ joined: Entrou
location:
all: Todos
- local: Local
remote: Remoto
- title: Local
- login_status: Estado de inรญcio de sessรฃo
- media_attachments: Anexos de media
+ title: Localizaรงรฃo
+ login_status: Situaรงรฃo da conta
+ media_attachments: Mรญdias anexadas
memorialize: Converter em memorial
- memorialized: Em memรณria
- memorialized_msg: Conta %{username} transformada com sucesso em memorial
+ memorialized: Convertidas em memorial
+ memorialized_msg: Transformou com sucesso %{username} em uma conta memorial
moderation:
- active: Activo
+ active: Ativo
all: Todos
pending: Pendente
silenced: Silenciados
- suspended: Supensos
+ suspended: Banidos
title: Moderaรงรฃo
moderation_notes: Notas de moderaรงรฃo
- most_recent_activity: Actividade mais recente
+ most_recent_activity: Atividade mais recente
most_recent_ip: IP mais recente
- no_account_selected: Nenhuma conta foi alterada porque nenhuma foi selecionada
- no_limits_imposed: Sem limites impostos
+ no_account_selected: Nenhuma conta foi alterada, pois nenhuma conta foi selecionada
+ no_limits_imposed: Nenhum limite imposto
not_subscribed: Nรฃo inscrito
- pending: Pendente de revisรฃo
- perform_full_suspension: Fazer suspensรฃo completa
+ pending: Revisรฃo pendente
+ perform_full_suspension: Banir
promote: Promover
protocol: Protocolo
public: Pรบblico
- push_subscription_expires: A Inscriรงรฃo PuSH expira
+ push_subscription_expires: Inscriรงรฃo PuSH expira
redownload: Atualizar perfil
- redownloaded_msg: Atualizado com sucesso o perfil de %{username} da origem
- reject: Rejeitar
- reject_all: Rejeitar todas
- rejected_msg: Inscriรงรฃo de %{username} rejeitada com sucesso
- remove_avatar: Remover a imagem de perfil
- remove_header: Remover o cabeรงalho
- removed_avatar_msg: Imagem de perfil de %{username} removida com sucesso
- removed_header_msg: Imagem de cabeรงalho de %{username} removida com sucesso
+ redownloaded_msg: Atualizado com sucesso o perfil de %{username} a partir da origem
+ reject: Vetar
+ reject_all: Vetar tudo
+ rejected_msg: Rejeitado com sucesso o pedido de registro de %{username}
+ remove_avatar: Remover imagem de perfil
+ remove_header: Remover capa
+ removed_avatar_msg: Removida com sucesso a imagem de avatar de %{username}
+ removed_header_msg: Removida com sucesso a imagem de capa de %{username}
resend_confirmation:
- already_confirmed: Este utilizador jรก estรก confirmado
- send: Reenviar um email de confirmaรงรฃo
- success: Email de confirmaรงรฃo enviado com sucesso!
- reset: Restaurar
- reset_password: Reset palavra-passe
- resubscribe: Reinscrever
+ already_confirmed: Este usuรกrio jรก estรก confirmado
+ send: Reenviar o e-mail de confirmaรงรฃo
+ success: E-mail de confirmaรงรฃo enviado com sucesso!
+ reset: Redefinir
+ reset_password: Redefinir senha
+ resubscribe: Reinscrever-se
role: Permissรตes
roles:
- admin: Administrador(a)
+ admin: Administrador
moderator: Moderador
- staff: Equipa
- user: Utilizador
+ staff: Equipe
+ user: Usuรกrio
search: Pesquisar
- search_same_email_domain: Outros utilizadores com o mesmo domรญnio de email
- search_same_ip: Outros utilizadores com o mesmo IP
- sensitive: Marcar como sensรญvel
- sensitized: marcada como sensรญvel
- shared_inbox_url: URL da caixa de entrada compartilhada
+ search_same_email_domain: Outros usuรกrios com o mesmo domรญnio de e-mail
+ search_same_ip: Outros usuรกrios com o mesmo IP
+ sensitive: Sensรญveis
+ sensitized: marcadas como sensรญveis
+ shared_inbox_url: Link da caixa de entrada compartilhada
show:
- created_reports: Relatรณrios gerados por esta conta
- targeted_reports: Relatรณrios feitos sobre esta conta
- silence: Silรชncio
- silenced: Silenciada
- statuses: Status
+ created_reports: Denรบncias desta conta
+ targeted_reports: Denรบncias sobre esta conta
+ silence: Silenciar
+ silenced: Silenciado
+ statuses: Toots
subscribe: Inscrever-se
- suspended: Suspensa
- suspension_irreversible: Os dados desta conta foram eliminados irreversivelmente. Pode cancelar a suspensรฃo da conta para tornรก-la utilizรกvel, mas ela nรฃo irรก recuperar os dados que possuรญa anteriormente.
- suspension_reversible_hint_html: A conta foi suspensa e os dados serรฃo totalmente eliminados em %{date}. Atรฉ lรก, a conta poderรก ser recuperada sem quaisquer efeitos negativos. Se deseja eliminar todos os dados desta conta imediatamente, pode fazรช-lo em baixo.
- time_in_queue: Aguardando na fila %{time}
+ suspended: Banido
+ suspension_irreversible: Os dados desta conta foram excluรญdos de forma irreversรญvel. Vocรช pode remover a suspensรฃo da conta para tornรก-la utilizรกvel, mas ela nรฃo irรก recuperar nenhum dado que ela possuรญa anteriormente.
+ suspension_reversible_hint_html: A conta foi suspensa e os dados serรฃo totalmente removidos em %{date}. Atรฉ lรก, a conta pode ser restaurada sem nenhum efeito negativo. Se vocรช deseja remover todos os dados da conta imediatamente, vocรช pode fazer isso abaixo.
+ time_in_queue: Esperando na fila por %{time}
title: Contas
unconfirmed_email: E-mail nรฃo confirmado
- undo_sensitized: Desmarcar como sensรญvel
- undo_silenced: Desfazer silenciar
- undo_suspension: Desfazer supensรฃo
- unsilenced_msg: Removeu com sucesso as limitaรงรตes da conta %{username}
+ undo_sensitized: Desfazer sensรญvel
+ undo_silenced: Desfazer silรชncio
+ undo_suspension: Desbanir
+ unsilenced_msg: Removidas com sucesso as limitaรงรตes da conta de %{username}
unsubscribe: Cancelar inscriรงรฃo
- unsuspended_msg: Removeu com sucesso a suspensรฃo da conta %{username}
- username: Utilizador
- view_domain: Ver resumo do domรญnio
- warn: Aviso
+ unsuspended_msg: Removida com sucesso a suspensรฃo da conta de %{username}
+ username: Nome de usuรกrio
+ view_domain: Ver resumo para o domรญnio
+ warn: Notificar
web: Web
- whitelisted: Estรก na lista branca
+ whitelisted: Permitido
action_logs:
action_types:
- assigned_to_self_report: Atribuir Relatรณrio
- change_email_user: Alterar E-mail do Utilizador
- confirm_user: Confirmar Utilizador
+ assigned_to_self_report: Adicionar relatรณrio
+ change_email_user: Editar e-mail do usuรกrio
+ confirm_user: Confirmar Usuรกrio
create_account_warning: Criar Aviso
create_announcement: Criar Anรบncio
create_custom_emoji: Criar Emoji Personalizado
- create_domain_allow: Criar Permissรฃo de Domรญnio
+ create_domain_allow: Adicionar domรญnio permitido
create_domain_block: Criar Bloqueio de Domรญnio
create_email_domain_block: Criar Bloqueio de Domรญnio de E-mail
create_ip_block: Criar regra de IP
- create_unavailable_domain: Criar Domรญnio Indisponรญvel
- demote_user: Despromover Utilizador
- destroy_announcement: Eliminar Anรบncio
- destroy_custom_emoji: Eliminar Emoji Personalizado
- destroy_domain_allow: Eliminar Permissรฃo de Domรญnio
- destroy_domain_block: Eliminar Bloqueio de Domรญnio
- destroy_email_domain_block: Eliminar Bloqueio de Domรญnio de E-mail
- destroy_ip_block: Eliminar regra de IP
- destroy_status: Eliminar Publicaรงรฃo
- destroy_unavailable_domain: Eliminar Domรญnio Indisponรญvel
- disable_2fa_user: Desativar 2FA
+ demote_user: Rebaixar usuรกrio
+ destroy_announcement: Excluir anรบncio
+ destroy_custom_emoji: Excluir emoji personalizado
+ destroy_domain_allow: Excluir domรญnio permitido
+ destroy_domain_block: Excluir Bloqueio de Domรญnio
+ destroy_email_domain_block: Excluir bloqueio de domรญnio de e-mail
+ destroy_ip_block: Excluir regra de IP
+ destroy_status: Excluir Status
+ disable_2fa_user: Desativar autenticaรงรฃo de dois fatores
disable_custom_emoji: Desativar Emoji Personalizado
- disable_user: Desativar Utilizador
+ disable_user: Desativar usuรกrio
enable_custom_emoji: Ativar Emoji Personalizado
- enable_user: Ativar Utilizador
- memorialize_account: Memorizar Conta
- promote_user: Promover Utilizador
- remove_avatar_user: Remover Imagem de Perfil
+ enable_user: Ativar usuรกrio
+ memorialize_account: Converter conta em memorial
+ promote_user: Promover usuรกrio
+ remove_avatar_user: Remover Avatar
reopen_report: Reabrir Relatรณrio
- reset_password_user: Repor Password
+ reset_password_user: Redefinir a senha
resolve_report: Resolver Relatรณrio
- sensitive_account: Marcar a media na sua conta como sensรญvel
- silence_account: Silenciar Conta
+ sensitive_account: Marcar a mรญdia na sua conta como sensรญvel
+ silence_account: Silenciar conta
suspend_account: Suspender Conta
- unassigned_report: Desatribuir Relatรณrio
- unsensitive_account: Desmarcar a media na sua conta como sensรญvel
- unsilence_account: Deixar de Silenciar Conta
- unsuspend_account: Retirar Suspensรฃo ร  Conta
- update_announcement: Atualizar Anรบncio
- update_custom_emoji: Atualizar Emoji Personalizado
- update_domain_block: Atualizar Bloqueio de Domรญnio
- update_status: Atualizar Estado
+ unassigned_report: Remover relatรณrio
+ unsensitive_account: Desmarcar a mรญdia na sua conta como sensรญvel
+ unsilence_account: Desfazer silenciar conta
+ unsuspend_account: Remover suspensรฃo de conta
+ update_announcement: Editar anรบncio
+ update_custom_emoji: Editar Emoji Personalizado
+ update_domain_block: Atualizar bloqueio de domรญnio
+ update_status: Editar Status
actions:
- assigned_to_self_report_html: "%{name} atribuiu o relatรณrio %{target} a si prรณprio"
- change_email_user_html: "%{name} alterou o endereรงo de e-mail do utilizador %{target}"
- confirm_user_html: "%{name} confirmou o endereรงo de e-mail do utilizador %{target}"
create_account_warning_html: "%{name} enviou um aviso para %{target}"
- create_announcement_html: "%{name} criou o novo anรบncio %{target}"
- create_custom_emoji_html: "%{name} carregou o novo emoji %{target}"
- create_domain_allow_html: "%{name} habilitou a federaรงรฃo com o domรญnio %{target}"
create_domain_block_html: "%{name} bloqueou o domรญnio %{target}"
- create_email_domain_block_html: "%{name} bloqueou o domรญnio de e-mail %{target}"
- create_ip_block_html: "%{name} criou regra para o IP %{target}"
- create_unavailable_domain_html: "%{name} parou a entrega ao domรญnio %{target}"
- demote_user_html: "%{name} despromoveu o utilizador %{target}"
- destroy_announcement_html: "%{name} eliminou o anรบncio %{target}"
- destroy_custom_emoji_html: "%{name} destruiu o emoji %{target}"
- destroy_domain_allow_html: "%{name} desabilitou a federaรงรฃo com o domรญnio %{target}"
- destroy_domain_block_html: "%{name} desbloqueou o domรญnio %{target}"
- destroy_email_domain_block_html: "%{name} desbloqueou o domรญnio de e-mail %{target}"
- destroy_ip_block_html: "%{name} eliminou regra para o IP %{target}"
- destroy_status_html: "%{name} removeu a publicaรงรฃo de %{target}"
- destroy_unavailable_domain_html: "%{name} retomou a entrega ao domรญnio %{target}"
- disable_2fa_user_html: "%{name} desativou o requerimento de autenticaรงรฃo em dois passos para o utilizador %{target}"
- disable_custom_emoji_html: "%{name} desabilitou o emoji %{target}"
- disable_user_html: "%{name} desativou o acesso para o utilizador %{target}"
- enable_custom_emoji_html: "%{name} habilitou o emoji %{target}"
- enable_user_html: "%{name} ativou o acesso para o utilizador %{target}"
- memorialize_account_html: "%{name} transformou a conta de %{target} em um memorial"
- promote_user_html: "%{name} promoveu o utilizador %{target}"
- remove_avatar_user_html: "%{name} removeu a imagem de perfil de %{target}"
- reopen_report_html: "%{name} reabriu o relatรณrio %{target}"
- reset_password_user_html: "%{name} restabeleceu a palavra-passe do utilizador %{target}"
- resolve_report_html: "%{name} resolveu o relatรณrio %{target}"
- sensitive_account_html: "%{name} marcou a media de %{target} como sensรญvel"
- silence_account_html: "%{name} silenciou a conta de %{target}"
- suspend_account_html: "%{name} suspendeu a conta de %{target}"
- unassigned_report_html: "%{name} desatribuiu o realtรณrio %{target}"
- unsensitive_account_html: "%{name} desmarcou a media de %{target} como sensรญvel"
- unsilence_account_html: "%{name} desativou o silรชncio de %{target}"
- unsuspend_account_html: "%{name} desativou a suspensรฃo de %{target}"
- update_announcement_html: "%{name} atualizou o anรบncio %{target}"
- update_custom_emoji_html: "%{name} atualizou o emoji %{target}"
- update_domain_block_html: "%{name} atualizou o bloqueio de domรญnio para %{target}"
- update_status_html: "%{name} atualizou o estado de %{target}"
- deleted_status: "(publicaรงรฃo eliminada)"
- empty: Nรฃo foram encontrados registos.
+ create_email_domain_block_html: "%{name} bloqueou do domรญnio de e-mail %{target}"
+ deleted_status: "(status excluรญdo)"
+ empty: Nenhum registro encontrado.
filter_by_action: Filtrar por aรงรฃo
- filter_by_user: Filtrar por utilizador
- title: Registo de auditoria
+ filter_by_user: Filtrar por usuรกrio
+ title: Auditar histรณrico
announcements:
- destroyed_msg: Anรบncio eliminado com sucesso!
+ destroyed_msg: Anรบncio excluรญdo com sucesso!
edit:
title: Editar anรบncio
- empty: Nenhum anรบncio encontrado.
- live: Em exibiรงรฃo
+ empty: Sem anรบncios.
+ live: Ao vivo
new:
create: Criar anรบncio
title: Novo anรบncio
@@ -326,184 +286,158 @@
scheduled_for: Agendado para %{time}
scheduled_msg: Anรบncio agendado para publicaรงรฃo!
title: Anรบncios
- unpublish: Anular publicaรงรฃo
- unpublished_msg: Anรบncio retirado de exibiรงรฃo com sucesso!
+ unpublish: Cancelar publicaรงรฃo
+ unpublished_msg: Anรบncio despublicado com sucesso!
updated_msg: Anรบncio atualizado com sucesso!
custom_emojis:
assign_category: Atribuir categoria
by_domain: Domรญnio
copied_msg: Cรณpia local do emoji criada com sucesso
copy: Copiar
- copy_failed_msg: Nรฃo foi possรญvel criar uma cรณpia local deste emoji
+ copy_failed_msg: Nรฃo foi possรญvel criar cรณpia local do emoji
create_new_category: Criar nova categoria
created_msg: Emoji criado com sucesso!
- delete: Eliminar
- destroyed_msg: Emoji destruรญdo com sucesso!
+ delete: Excluir
+ destroyed_msg: Emoji excluรญdo com sucesso!
disable: Desativar
disabled: Desativado
- disabled_msg: Desativado com sucesso este emoji
- emoji: Emoji
+ disabled_msg: Emoji desativado com sucesso
enable: Ativar
enabled: Ativado
- enabled_msg: Ativado com sucesso este emoji
+ enabled_msg: Emoji ativado com sucesso
image_hint: PNG de atรฉ 50KB
- list: Lista
+ list: Listar
listed: Listado
new:
- title: Adicionar novo emoji customizado
- not_permitted: Nรฃo estรก autorizado a executar esta aรงรฃo
+ title: Adicionar novo emoji personalizado
+ not_permitted: Vocรช nรฃo tem permissรฃo para executar esta aรงรฃo
overwrite: Sobrescrever
- shortcode: Cรณdigo de atalho
- shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanumรฉricos e underscores
- title: Emojis customizados
- uncategorized: Sem categoria
+ shortcode: Atalho
+ shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanumรฉricos e underlines ("_")
+ title: Emojis personalizados
+ uncategorized: Nรฃo categorizado
unlist: Nรฃo listar
- unlisted: Nรฃo listado
+ unlisted: Nรฃo-listado
update_failed_msg: Nรฃo foi possรญvel atualizar esse emoji
updated_msg: Emoji atualizado com sucesso!
upload: Enviar
dashboard:
authorized_fetch_mode: Modo seguro
- backlog: trabalhos atrasados
+ backlog: tarefas na fila
config: Configuraรงรฃo
- feature_deletions: Eliminaรงรตes da conta
- feature_invites: Links de convites
+ feature_deletions: Exclusรฃo de contas
+ feature_invites: Convites
feature_profile_directory: Diretรณrio de perfis
- feature_registrations: Registos
+ feature_registrations: Novas contas
feature_relay: Repetidor da federaรงรฃo
- feature_timeline_preview: Prรฉ-visualizaรงรฃo da cronologia
- features: Componentes
- hidden_service: Federaรงรฃo com serviรงos escondidos
- open_reports: relatรณrios abertos
- pending_tags: hashtags a aguardar revisรฃo
- pending_users: utilizadores a aguardar revisรฃo
- recent_users: Utilizadores recentes
- search: Pesquisa com texto completo
- single_user_mode: Modo de utilizador รบnico
+ feature_timeline_preview: Prรฉvia da linha
+ features: Funcionalidades
+ hidden_service: Federaรงรฃo com serviรงos onion
+ open_reports: Denรบncias em aberto
+ pending_tags: hashtags pendentes
+ pending_users: usuรกrios pendentes
+ recent_users: Usuรกrios recentes
+ search: Pesquisa em texto
+ single_user_mode: Modo de usuรกrio รบnico
software: Software
- space: Utilizaรงรฃo do espaรงo
- title: Painel de controlo
- total_users: total de utilizadores
- trends: Tendรชncias
- week_interactions: interacรงรตes desta semana
- week_users_active: activo esta semana
- week_users_new: utilizadores nesta semana
- whitelist_mode: Modo lista branca
+ space: Uso de espaรงo em disco
+ title: Painel de controle
+ total_users: usuรกrios no total
+ trends: Em alta
+ week_interactions: interaรงรตes essa semana
+ week_users_active: ativos essa semana
+ week_users_new: usuรกrios essa semana
+ whitelist_mode: Modo lista de permitidos
domain_allows:
- add_new: Colocar domรญnio na lista branca
- created_msg: Domรญnio foi adicionado ร  lista branca com sucesso
- destroyed_msg: Domรญnio foi removido da lista branca
- undo: Remover da lista branca
+ add_new: Permitir domรญnio
+ created_msg: Domรญnio foi permitido
+ destroyed_msg: Domรญnio foi bloqueado
+ undo: Bloquear
domain_blocks:
- add_new: Adicionar novo
- created_msg: Bloqueio do domรญnio estรก a ser processado
- destroyed_msg: Bloqueio de domรญnio estรก a ser removido
+ add_new: Adicionar novo bloqueio de domรญnio
+ created_msg: Domรญnio estรก sendo bloqueado
+ destroyed_msg: Domรญnio desbloqueado
domain: Domรญnio
edit: Editar bloqueio de domรญnio
- existing_domain_block_html: Vocรช jรก impรดs limites mais restritivos a %{name}, รฉ necessรกrio primeiro <a href="%{unblock_url}">desbloqueรก-lo</a>.
+ existing_domain_block_html: Vocรช jรก impรดs limites mais estritos em %{name}, vocรช precisa <a href="%{unblock_url}">desbloqueรก-lo</a> primeiro.
new:
create: Criar bloqueio
- hint: O bloqueio de dominio nรฃo vai previnir a criaรงรฃo de entradas na base de dados, mas irรก retroativamente e automaticamente aplicar mรฉtodos de moderaรงรฃo especรญfica nessas contas.
+ hint: O bloqueio de domรญnio nรฃo vai prevenir a criaรงรฃo de entradas de contas na base de dados, mas vai retroativamente e automaticamente aplicar mรฉtodos especรญficos de moderaรงรฃo nessas contas.
severity:
- desc_html: "<strong>Silenciar</strong> irรก fazer com que as publicaรงรตes dessa conta sejam invisรญveis para quem nรฃo a segue. <strong>Supender</strong> irรก eliminar todo o conteรบdo guardado dessa conta, media e informaรงรฃo de perfil. Use <strong>Nenhum</strong> se apenas deseja rejeitar arquivos de media."
+ desc_html: "<strong>Silenciar</strong> vai fazer os posts da conta invisรญveis para qualquer um que nรฃo os esteja seguindo. <strong>Suspender</strong> vai remover todo o conteรบdo, mรญdia, e dados de perfil da conta. Use <strong>Nenhum</strong> se vocรช sรณ quer rejeitar arquivos de mรญdia."
noop: Nenhum
silence: Silenciar
- suspend: Suspender
+ suspend: Banir
title: Novo bloqueio de domรญnio
obfuscate: Ofuscar nome de domรญnio
- obfuscate_hint: Ofuscar parcialmente o nome de domรญnio na lista, se estiverem habilitadas as limitaรงรตes na publicaรงรฃo da lista de domรญnios
private_comment: Comentรกrio privado
- private_comment_hint: Comentรกrio sobre essa limitaรงรฃo de domรญnio para uso interno pelos moderadores.
+ private_comment_hint: Comente sobre essa restriรงรฃo ao domรญnio para uso interno dos moderadores.
public_comment: Comentรกrio pรบblico
- public_comment_hint: Comentรกrio sobre essa limitaรงรฃo de domรญnio para o pรบblico geral, se ativada a divulgaรงรฃo da lista de limitaรงรตes de domรญnio.
- reject_media: Rejeitar ficheiros de media
- reject_media_hint: Remove arquivos de media armazenados localmente e rejeita descarregar novos arquivos no futuro. Irrelevante para suspensรตes
- reject_reports: Rejeitar relatรณrios
- reject_reports_hint: Ignorar todos os relatรณrios vindos deste domรญnio. Irrelevantes para efectuar suspensรตes
- rejecting_media: a rejeitar ficheiros de media
- rejecting_reports: a rejeitar relatรณrios
+ public_comment_hint: Comente sobre essa restriรงรฃo ao domรญnio para o pรบblico geral, caso a divulgaรงรฃo da lista de bloqueio esteja ativada.
+ reject_media: Rejeitar arquivos de mรญdia
+ reject_media_hint: Remove arquivos de mรญdia armazenados localmente e recusa fazer download de qualquer um no futuro. Irrelevante para suspensรตes
+ reject_reports: Rejeitar denรบncias
+ reject_reports_hint: Ignora todas as denรบncias vindo deste domรญnio. Irrelevante para suspensรตes
+ rejecting_media: rejeitando arquivos de mรญdia
+ rejecting_reports: rejeitando denรบncias
severity:
silence: silenciado
- suspend: suspenso
+ suspend: banido
show:
affected_accounts:
- one: Uma conta na base de dados afectada
- other: "%{count} contas na base de dados afectadas"
+ one: Uma conta no banco de dados foi afetada
+ other: "%{count} contas no banco de dados foram afetadas"
retroactive:
- silence: Nรฃo silenciar contas afetadas existentes deste domรญnio
- suspend: Nรฃo suspender todas as contas existentes nesse domรญnio
- title: Remover o bloqueio de domรญnio de %{domain}
- undo: Anular
- undo: Anular
+ silence: Dessilenciar contas existentes afetadas deste domรญnio
+ suspend: Remover a suspensรฃo das contas afetadas deste domรญnio
+ title: Desfazer bloqueio de domรญnio para %{domain}
+ undo: Desfazer
+ undo: Desfazer bloqueio de domรญnio
view: Ver domรญnios bloqueados
email_domain_blocks:
add_new: Adicionar novo
- created_msg: Bloqueio de domรญnio de email criado com sucesso
- delete: Eliminar
- destroyed_msg: Bloqueio de domรญnio de email excluรญdo com sucesso
+ created_msg: Domรญnio de e-mail adicionado ร  lista negra com sucesso
+ delete: Excluir
+ destroyed_msg: Domรญnio de e-mail excluรญdo da lista negra com sucesso
domain: Domรญnio
- empty: Nenhum domรญnio de e-mail atualmente na lista negra.
+ empty: Nenhum domรญnio de e-mail atualmente bloqueado.
from_html: de %{domain}
new:
create: Adicionar domรญnio
- title: Novo bloqueio de domรญnio de email
- title: Bloqueio de Domรญnio de Email
- follow_recommendations:
- description_html: "<strong>Recomendaรงรตes de quem seguir ajudam novos utilizadores a encontrar conteรบdo interessante rapidamente.</strong>. Quando um utilizador nรฃo interage com outros o suficiente para formar recomendaรงรตes personalizadas, estas contas sรฃo recomendadas. Elas sรฃo recalculadas diariamente a partir de uma mistura de contas com mais atividade recente e maior nรบmero de seguidores locais para um determinado idioma."
- language: Para o idioma
- status: Estado
- suppress: Suprimir recomendaรงรฃo de contas a seguir
- suppressed: Suprimida
- title: Seguir recomendaรงรตes
- unsuppress: Restaurar recomendaรงรตes de contas a seguir
+ title: Nova entrada de lista negra de e-mail
+ title: Lista de negra de e-mail
instances:
- back_to_all: Todas
- back_to_limited: Limitadas
back_to_warning: Aviso
by_domain: Domรญnio
- delivery:
- all: Todas
- clear: Limpar erros de entrega
- restart: Reiniciar entrega
- stop: Parar entrega
- title: Entrega
- unavailable: Indisponรญvel
- unavailable_message: Entrega indisponรญvel
- warning: Aviso
- warning_message:
- one: Falhou entrega %{count} dia
- other: Falhou entrega %{count} dias
- delivery_available: Entrega disponรญvel
- delivery_error_days: Dias de erro de entrega
- delivery_error_hint: Se a entrega nรฃo for possรญvel durante %{count} dias, serรก automaticamente marcada como nรฃo realizรกvel.
- empty: Nรฃo foram encontrados domรญnios.
+ delivery_available: Envio disponรญvel
+ empty: Nenhum domรญnio encontrado.
known_accounts:
one: "%{count} conta conhecida"
other: "%{count} contas conhecidas"
moderation:
- all: Todas
- limited: Limitadas
+ all: Todos
+ limited: Limitados
title: Moderaรงรฃo
- private_comment: Comentรกrios privados
- public_comment: Comentรกrios pรบblicos
- title: Instรขncias conhecidas
- total_blocked_by_us: Bloqueadas por nรณs
- total_followed_by_them: Seguidas por eles
- total_followed_by_us: Seguidas por nรณs
- total_reported: Relatรณrios sobre eles
- total_storage: Anexos de media
+ private_comment: Comentรกrio privado
+ public_comment: Comentรกrio pรบblico
+ title: Federaรงรฃo
+ total_blocked_by_us: Bloqueado por nรณs
+ total_followed_by_them: Seguidos por eles
+ total_followed_by_us: Seguidos por nรณs
+ total_reported: Denรบncias sobre eles
+ total_storage: Mรญdias anexadas
invites:
- deactivate_all: Desactivar todos
+ deactivate_all: Desativar todos
filter:
all: Todos
- available: Disponรญveis
- expired: Expirados
+ available: Disponรญvel
+ expired: Expirado
title: Filtro
title: Convites
ip_blocks:
add_new: Criar regra
created_msg: Nova regra de IP adicionada com sucesso
- delete: Eliminar
+ delete: Excluir
expires_in:
'1209600': 2 semanas
'15778476': 6 meses
@@ -521,40 +455,39 @@
title: Relaรงรตes de %{acct}
relays:
add_new: Adicionar novo repetidor
- delete: Eliminar
- description_html: Um <strong>repetidor de federaรงรฃo</strong> รฉ um servidor intermediรกrio que troca grandes volumes de publicaรงรตes pรบblicas entre instรขncias que o subscrevem e publicam. <strong>Ele pode ajudar pequenas e medias instรขncias a descobrir conteรบdo do fediverso</strong> que, de outro modo, exigiria que os utilizadores locais seguissem manualmente outras pessoas em instรขncias remotas.
- disable: Desactivar
- disabled: Desactivado
- enable: Activar
- enable_hint: Uma vez ativado, a tua instรขncia irรก subscrever a todas as publicaรงรตes deste repetidor e irรก comeรงar a enviar as suas publicaรงรตes pรบblicas para ele.
+ delete: Excluir
+ description_html: Um <strong>repetidor de federaรงรฃo</strong> รฉ um servidor intermediรกrio que troca um grande volume de toots pรบblicos entre instรขncias que se inscrevem e publicam nele. <strong>O repetidor pode ser usado para ajudar instรขncias pequenas e mรฉdias a descobrir conteรบdo pelo fediverso</strong>, que normalmente precisariam que usuรกrios locais manualmente seguissem outras pessoas em instรขncias remotas.
+ disable: Desativar
+ disabled: Desativado
+ enable: Ativar
+ enable_hint: Uma vez ativado, sua instรขncia se inscreverรก para receber todos os toots pรบblicos desse repetidor; E vai comeรงar a enviar todos os toots pรบblicos desta instรขncia para o repetidor.
enabled: Ativado
- inbox_url: URL do repetidor
- pending: ร€ espera da aprovaรงรฃo do repetidor
- save_and_enable: Guardar e ativar
- setup: Configurar uma ligaรงรฃo ao repetidor
- signatures_not_enabled: Relays nรฃo funcionarรฃo corretamente enquanto o modo seguro ou o modo lista branca estiverem ativados
- status: Estado
- title: Retransmissores
+ inbox_url: Link do repetidor
+ pending: Esperando pela aprovaรงรฃo do repetidor
+ save_and_enable: Salvar e ativar
+ setup: Configurar uma conexรฃo de repetidor
+ signatures_not_enabled: Repetidores nรฃo funcionarรฃo adequadamente enquanto o modo seguro ou o modo lista de permitidos estiverem ativos
+ title: Repetidores
report_notes:
- created_msg: Relatรณrio criado com sucesso!
- destroyed_msg: Nota de relatรณrio eliminada com sucesso!
+ created_msg: Nota de denรบncia criada com sucesso!
+ destroyed_msg: Nota de denรบncia excluรญda com sucesso!
reports:
account:
notes:
one: "%{count} nota"
other: "%{count} notas"
reports:
- one: "%{count} relatรณrio"
- other: "%{count} relatรณrios"
- action_taken_by: Aรงรฃo tomada por
- are_you_sure: Tens a certeza?
- assign_to_self: Atribuรญ-me a mim
- assigned: Atribuรญdo ao moderador
- by_target_domain: Domรญnio da conta reportada
+ one: "%{count} denรบncia"
+ other: "%{count} denรบncias"
+ action_taken_by: Atitude tomada por
+ are_you_sure: Vocรช tem certeza?
+ assign_to_self: Pegar
+ assigned: Moderador responsรกvel
+ by_target_domain: Domรญnio da conta denunciada
comment:
none: Nenhum
- created_at: Relatado
- forwarded: Encaminhado
+ created_at: Denunciado
+ forwarded: Encaminhados
forwarded_to: Encaminhado para %{domain}
mark_as_resolved: Marcar como resolvido
mark_as_unresolved: Marcar como nรฃo resolvido
@@ -562,423 +495,430 @@
create: Adicionar nota
create_and_resolve: Resolver com nota
create_and_unresolve: Reabrir com nota
- delete: Eliminar
- placeholder: Descreve as aรงรตes que foram tomadas ou quaisquer outras atualizaรงรตes relacionadas...
- reopen: Reabrir relatรณrio
+ delete: Excluir
+ placeholder: Descreva que aรงรตes foram tomadas, ou quaisquer outras atualizaรงรตes relacionadasโ€ฆ
+ reopen: Reabrir denรบncia
report: 'Denรบncia #%{id}'
reported_account: Conta denunciada
- reported_by: Reportado por
+ reported_by: Denunciada por
resolved: Resolvido
- resolved_msg: Relatรณrio resolvido com sucesso!
- status: Estado
- title: Relatรณrios
- unassign: Nรฃo atribuir
- unresolved: Por resolver
+ resolved_msg: Denรบncia resolvida com sucesso!
+ title: Denรบncias
+ unassign: Largar
+ unresolved: Nรฃo resolvido
updated_at: Atualizado
rules:
add_new: Adicionar regra
- delete: Eliminar
- description_html: Embora a maioria afirme ter lido e concordado com os termos de serviรงo, geralmente as pessoas sรณ leem depois de surgir um problema. <strong>Dรช uma olhada nas regras do seu servidor fornecendo-as em uma lista de marcadores planos.</strong> Tente manter as regras individuais curtas e simples, mas tente tambรฉm nรฃo dividi-las em muitos itens separados.
+ delete: Deletar
+ description_html: Embora a maioria afirme ter lido e concordado com os termos de serviรงo, geralmente as pessoas sรณ leem depois de surgir um problema. <strong>Faรงa com que seja mais fรกcil ver as regras do seu servidor rapidamente fornecendo-as em uma lista.</strong> Tente manter cada regra curta e simples, mas tambรฉm tente nรฃo dividi-las em muitos itens separados.
edit: Editar regra
- empty: Nenhuma regra de instรขncia foi ainda definida.
- title: Regras da instรขncia
+ empty: Nenhuma regra do servidor foi definida.
+ title: Regras do servidor
settings:
activity_api_enabled:
- desc_html: Contagem semanais de publicaรงรตes locais, utilizadores activos e novos registos
- title: Publicar estatรญsticas agregadas sobre atividade dos utilizadores
+ desc_html: Contagem de toots locais, usuรกrios ativos e novos usuรกrios semanalmente
+ title: Publicar estatรญsticas agregadas sobre atividade de usuรกrios
bootstrap_timeline_accounts:
- desc_html: Separa os nomes de utilizadores por vรญrgulas. Funciona apenas com contas locais e desbloqueadas. O padrรฃo quando vazio sรฃo todos os administradores locais.
- title: Seguidores predefinidos para novas contas
+ desc_html: Separe nomes de usuรกrio atravรฉs de vรญrgulas. Funciona apenas com contas locais e destrancadas. O padrรฃo quando vazio sรฃo todos os administradores locais.
+ title: Usuรกrios a serem seguidos por padrรฃo por novas contas
contact_information:
- email: Inserir um endereรงo de email para tornar pรบblico
- username: Insira um nome de utilizador
+ email: E-mail
+ username: Usuรกrio de contato
custom_css:
- desc_html: Modificar a aparรชncia com CSS carregado em cada pรกgina
+ desc_html: Alterar o visual com CSS carregado em todas as pรกginas
title: CSS personalizado
default_noindex:
- desc_html: Afeta todos os utilizadores que nรฃo alteraram esta configuraรงรฃo
- title: Desactivar, por omissรฃo, a indexaรงรฃo de utilizadores por parte dos motores de pesquisa
+ desc_html: Afeta qualquer usuรกrio que nรฃo tenha alterado esta configuraรงรฃo manualmente
+ title: Optar por excluir usuรกrios da indexaรงรฃo de mecanismos de pesquisa por padrรฃo
domain_blocks:
- all: Para toda a gente
+ all: Para todos
disabled: Para ninguรฉm
title: Mostrar domรญnios bloqueados
- users: Para utilizadores locais que se encontrem autenticados
+ users: Para usuรกrios locais logados
domain_blocks_rationale:
title: Mostrar motivo
hero:
- desc_html: Apresentado na primeira pรกgina. Pelo menos 600x100px recomendados. Quando nรฃo รฉ definido, รฉ apresentada a miniatura da instรขncia
- title: Imagem Hero
+ desc_html: Aparece na pรกgina inicial. Recomendado ao menos 600x100px. Se nรฃo estiver definido, a miniatura da instรขncia รฉ usada no lugar
+ title: Imagem de capa
mascot:
- desc_html: Apresentada em mรบltiplas pรกginas. Pelo menos 293x205px recomendados. Quando nรฃo รฉ definida, รฉ apresentada a mascote predefinida
- title: Imagem da mascote
+ desc_html: Mostrado em diversas pรกginas. Recomendado ao menos 293ร—205px. Quando nรฃo estรก definido, o mascote padrรฃo รฉ mostrado
+ title: Imagem do mascote
peers_api_enabled:
- desc_html: Nomes de domรญnio que esta instรขncia encontrou no fediverso
+ desc_html: Nomes de domรญnio que essa instรขncia encontrou no fediverso
title: Publicar lista de instรขncias descobertas
preview_sensitive_media:
- desc_html: A pre-visualizaรงรฃo de links noutros sites irรก apresentar uma miniatura, mesmo que a media seja marcada como sensรญvel
- title: Mostrar media sensรญvel em pre-visualizaรงรตes OpenGraph
+ desc_html: A prรฉvia do link em outros sites vai incluir uma miniatura mesmo se a mรญdia estiver marcada como sensรญvel
+ title: Mostrar mรญdia sensรญvel em prรฉvias OpenGraph
profile_directory:
- desc_html: Permite aos utilizadores serem descobertos
- title: Ativar directรณrio do perfil
+ desc_html: Permitir que usuรกrios possam ser descobertos
+ title: Ativar diretรณrio de perfis
registrations:
closed_message:
- desc_html: Mostrar na pรกgina inicial quando registos estรฃo encerrados<br/>Podes usar tags HTML
- title: Mensagem de registos encerrados
+ desc_html: Mostrado na pรกgina inicial quando a instรขncia estรก fechada. Vocรช pode usar tags HTML
+ title: Mensagem de instรขncia fechada
deletion:
- desc_html: Permitir a qualquer utilizador eliminar a sua conta
- title: Permitir eliminar contas
+ desc_html: Permitir que qualquer um exclua a prรณpria conta
+ title: Exclusรฃo aberta de contas
min_invite_role:
disabled: Ninguรฉm
title: Permitir convites de
require_invite_text:
- desc_html: Quando os registos exigirem aprovaรงรฃo manual, faรงa o texto "Porque se quer juntar a nรณs?" da solicitaรงรฃo de convite obrigatรณrio, em vez de opcional
- title: Exigir que novos utilizadores preencham um texto de solicitaรงรฃo de convite
+ desc_html: Quando o cadastro de novas contas exigir aprovaรงรฃo manual, tornar obrigatรณrio, ao invรฉs de opcional, o texto de solicitaรงรฃo de convite em "Por que vocรช deseja criar uma conta aqui?"
+ title: Exigir que novos usuรกrios preencham um texto de solicitaรงรฃo de convite
registrations_mode:
modes:
- approved: Registo sujeito a aprovaรงรฃo
- none: Ninguรฉm se pode registar
- open: Qualquer pessoa se pode registar
- title: Modo de registo
+ approved: Aprovaรงรฃo necessรกria para criar conta
+ none: Ninguรฉm pode criar conta
+ open: Qualquer um pode criar conta
+ title: Modo de novos usuรกrios
show_known_fediverse_at_about_page:
- desc_html: Quando comutado, irรก mostrar a previsualizaรงรฃo de publicaรงรตes de todo o fediverse conhecido. De outro modo sรณ mostrarรก publicaรงรตes locais.
- title: Mostrar o fediverse conhecido na previsualizaรงรฃo da cronologia
+ desc_html: Quando ativado, mostra toots globais na prรฉvia da linha, se nรฃo, mostra somente toots locais
+ title: Mostrar toots globais na prรฉvia da linha
show_staff_badge:
- desc_html: Mostrar um crachรก da equipa na pรกgina de utilizador
- title: Mostrar crachรก da equipa
+ desc_html: Mostrar uma insรญgnia de Equipe na pรกgina de usuรกrio
+ title: Mostrar insรญgnia de equipe
site_description:
- desc_html: Mostrar como parรกgrafo na pรกgina inicial e usado como meta tag.Podes usar tags HTML, em particular <code>&lt;a&gt;</code> e <code>&lt;em&gt;</code>.
- title: Descriรงรฃo do site
+ desc_html: Parรกgrafo introdutรณrio na pรกgina inicial. Descreva o que faz esse servidor especial, e qualquer outra coisa de importante. Vocรช pode usar tags HTML, em especial <code>&lt;a&gt;</code> e <code>&lt;em&gt;</code>.
+ title: Descriรงรฃo da instรขncia
site_description_extended:
- desc_html: Mostrar na pรกgina de mais informaรงรตes<br/>Podes usar tags HTML
- title: Pรกgina de mais informaรงรตes
+ desc_html: Um รณtimo lugar para seu cรณdigo de conduta, regras, diretrizes e outras coisas para diferenciar a sua instรขncia. Vocรช pode usar tags HTML
+ title: Informaรงรฃo estendida personalizada
site_short_description:
- desc_html: Mostrada na barra lateral e em etiquetas de metadados. Descreve o que o Mastodon รฉ e o que torna esta instรขncia especial num รบnico parรกgrafo. Se deixada em branco, remete para a descriรงรฃo da instรขncia.
- title: Breve descriรงรฃo da instรขncia
+ desc_html: Mostrada na barra lateral e em etiquetas de metadados. Descreve o que รฉ o Mastodon e o que torna esta instรขncia especial num รบnico parรกgrafo. Se deixada em branco, รฉ substituรญdo pela descriรงรฃo da instรขncia.
+ title: Descriรงรฃo curta da instรขncia
site_terms:
- desc_html: Podes escrever a sua prรณpria polรญtica de privacidade, termos de serviรงo, entre outras coisas. Pode utilizar etiquetas HTML
+ desc_html: Vocรช pode escrever a sua prรณpria Polรญtica de Privacidade, Termos de Serviรงo, entre outras coisas. Vocรช pode usar tags HTML
title: Termos de serviรงo personalizados
- site_title: Tรญtulo do site
+ site_title: Nome da instรขncia
thumbnail:
- desc_html: Usada para visualizaรงรตes via OpenGraph e API. Recomenda-se 1200x630px
+ desc_html: Usada para prรฉvias via OpenGraph e API. Recomenda-se 1200x630px
title: Miniatura da instรขncia
timeline_preview:
- desc_html: Exibir a linha temporal pรบblica na pรกgina inicial
- title: Visualizaรงรฃo da linha temporal
+ desc_html: Mostra a linha do tempo pรบblica na pรกgina inicial e permite acesso da API ร  mesma sem autenticaรงรฃo
+ title: Permitir acesso nรฃo autenticado ร  linha pรบblica
title: Configuraรงรตes do site
trendable_by_default:
- desc_html: Afecta as hashtags que ainda nรฃo tenham sido proibidas
- title: Permitir hashtags em tendรชncia sem revisรฃo prรฉvia
+ desc_html: Afeta as hashtags que nรฃo foram reprovadas anteriormente
+ title: Permitir que hashtags fiquem em alta sem revisรฃo prรฉvia
trends:
- desc_html: Exibir publicamente hashtags atualmente em destaque que jรก tenham sido revistas anteriormente
- title: Hashtags em destaque
+ desc_html: Mostrar publicamente hashtags previamente revisadas que estรฃo em alta
+ title: Hashtags em alta
site_uploads:
- delete: Eliminar arquivo carregado
- destroyed_msg: Upload do site eliminado com sucesso!
+ delete: Excluir arquivo enviado
+ destroyed_msg: Upload do site excluรญdo com sucesso!
statuses:
back_to_account: Voltar para pรกgina da conta
batch:
- delete: Eliminar
- nsfw_off: NSFW OFF
- nsfw_on: NSFW ON
- deleted: Eliminado
- failed_to_execute: Falhou ao executar
+ delete: Excluir
+ nsfw_off: Desmarcar como sensรญvel
+ nsfw_on: Marcar como sensรญvel
+ deleted: Excluรญdos
+ failed_to_execute: Falha ao executar
media:
- title: Media
- no_media: Nรฃo hรก media
- no_status_selected: Nenhum estado foi alterado porque nenhum foi selecionado
- title: Estado das contas
- with_media: Com media
+ title: Mรญdia
+ no_media: Sem mรญdia
+ no_status_selected: Nenhum status foi modificado porque nenhum estava selecionado
+ title: Toots da conta
+ with_media: Com mรญdia
system_checks:
- database_schema_check:
- message_html: Existem migraรงรตes de base de dados pendentes. Por favor, execute-as para garantir que o aplicativo se comporte como esperado
rules_check:
- action: Gerir regras da instรขncia
- message_html: Nรฃo definiu nenhuma regra para a instรขncia.
- sidekiq_process_check:
- message_html: Nenhum processo Sidekiq em execuรงรฃo para a(s) fila(s) %{value}. Reveja a configuraรงรฃo do seu Sidekiq
+ message_html: Vocรช nรฃo definiu nenhuma regra de servidor.
tags:
- accounts_today: Usos รบnicos hoje
+ accounts_today: Usos รบnicos de hoje
accounts_week: Usos รบnicos desta semana
breakdown: Descriรงรฃo do consumo atual por fonte
- last_active: รšltima actividade
- most_popular: Mais popular
- most_recent: Mais recente
- name: Hashtag
- review: Estado da revisรฃo
- reviewed: Revista
- title: Hashtags
- trending_right_now: Tendรชncias agora
- unique_uses_today: "%{count} publicando hoje"
- unreviewed: Nรฃo revista
- updated_msg: Definiรงรตes de hashtags actualizadas com sucesso
+ last_active: รšltima atividade
+ most_popular: Mais populares
+ most_recent: Mais recentes
+ review: Status da revisรฃo
+ reviewed: Revisado
+ trending_right_now: Em alta no momento
+ unique_uses_today: "%{count} tootando hoje"
+ unreviewed: Nรฃo revisadas
+ updated_msg: Configuraรงรตes de hashtag atualizadas com sucesso
title: Administraรงรฃo
warning_presets:
add_new: Adicionar novo
- delete: Eliminar
- edit_preset: Editar o aviso predefinido
- empty: Ainda nรฃo definiu nenhum aviso predefinido.
- title: Gerir os avisos predefinidos
+ delete: Excluir
+ edit_preset: Editar o aviso prรฉ-definido
+ title: Gerenciar os avisos prรฉ-definidos
admin_mailer:
+ account_invitation:
+ subject: Vocรช foi convidado a entrar no Truth Social!
new_pending_account:
- body: Em baixo, estรฃo os detalhes da nova conta. Pode aprovar ou rejeitar esta inscriรงรฃo.
+ body: Os detalhes da nova conta estรฃo abaixo. Vocรช pode aprovar ou vetar.
subject: Nova conta para revisรฃo em %{instance} (%{username})
new_report:
- body: "%{reporter} relatou %{target}"
- body_remote: Alguรฉm de %{domain} relatou %{target}
- subject: Novo relatรณrio sobre %{instance} (#%{id})
+ body: "%{reporter} denunciou %{target}"
+ body_remote: Alguรฉm da instรขncia %{domain} reportou %{target}
+ subject: Nova denรบncia sobre %{instance} (#%{id})
new_trending_tag:
- body: 'A hashtag #%{name} estรก hoje a destacar-se, mas nรฃo foi anteriormente revista. Ela nรฃo serรก exibida publicamente a menos que vocรช o permita, ou limite-se a salvar o formulรกrio tal como estรก, para nunca mais ouvir falar dela.'
- subject: Nova hashtag para revisรฃo em %{instance} (#%{name})
+ body: 'A hashtag #%{name} estรก em alta hoje, mas nรฃo foi previamente revisada. Ela nรฃo estarรก visรญvel publicamente a menos que vocรช aprove, ou salve o formulรกrio do jeito que estรก para nunca mais ouvir falar dela.'
+ subject: Nova hashtag disponรญvel para revisรฃo em %{instance} (#%{name})
aliases:
- add_new: Criar pseudรณnimo
- created_msg: Criou com sucesso um novo pseudรณnimo. Pode agora iniciar a migraรงรฃo da conta antiga.
- deleted_msg: O pseudรณnimo foi eliminado com sucesso. Migrar dessa conta para esta nรฃo serรก mais possรญvel.
- empty: Nรฃo tem pseudรณnimos.
- hint_html: Se quiser mudar de outra conta para esta, pode criar aqui um pseudรณnimo, que รฉ necessรกrio antes de poder prosseguir com a migraรงรฃo de seguidores da conta antiga para esta. Esta aรงรฃo por si sรณ รฉ <strong>inofensiva e reversรญvel</strong>. <strong>A migraรงรฃo da conta รฉ iniciada a partir da conta antiga</strong>.
- remove: Desvincular pseudรณnimo
+ add_new: Criar alias
+ created_msg: Um novo alias foi criado com sucesso. Agora vocรช pode iniciar a mudanรงa da conta antiga.
+ deleted_msg: Alias removido com sucesso. Nรฃo serรก mais possรญvel se mudar daquela conta para esta conta.
+ empty: Vocรช nรฃo tem alias.
+ hint_html: Se vocรช quiser migrar de uma outra conta para esta, vocรช pode criar um alias aqui, o que รฉ necessรกrio antes que vocรช possa migrar os seguidores da conta antiga para esta. Esta aรงรฃo por si sรณ รฉ <strong>inofensiva e reversรญvel</strong>. <strong>A migraรงรฃo da conta รฉ iniciada pela conta antiga</strong>.
+ remove: Desvincular alias
appearance:
- advanced_web_interface: Interface web avanรงada
- advanced_web_interface_hint: 'Se quiser utilizar toda a largura do seu ecrรฃ, a interface web avanรงada permite-lhe configurar vรกrias colunas diferentes para ver tanta informaรงรฃo ao mesmo tempo quanto quiser: Pรกgina inicial, notificaรงรตes, cronologia federada, qualquer nรบmero de listas e hashtags.'
+ advanced_web_interface: Interface avanรงada de colunas
+ advanced_web_interface_hint: 'Se vocรช deseja usar toda a sua largura de tela, a interface avanรงada permite que vocรช configure muitas colunas diferentes para ver tantas informaรงรตes ao mesmo tempo quanto vocรช deseja: Pรกgina inicial, notificaรงรตes, linha local, linha global, qualquer nรบmero de listas e hashtags.'
animations_and_accessibility: Animaรงรตes e acessibilidade
- confirmation_dialogs: Caixas de confirmaรงรฃo
+ confirmation_dialogs: Diรกlogos de confirmaรงรฃo
discovery: Descobrir
localization:
body: Mastodon รฉ traduzido por voluntรกrios.
- guide_link: https://crowdin.com/project/mastodon/pt-PT
+ guide_link: https://br.crowdin.com/project/mastodon
guide_link_text: Todos podem contribuir.
sensitive_content: Conteรบdo sensรญvel
- toot_layout: Disposiรงรฃo do Toot
+ toot_layout: Layout do Toot
application_mailer:
notification_preferences: Alterar preferรชncias de e-mail
- salutation: "%{name},"
- settings: 'Alterar preferรชncias de email: %{link}'
+ settings: 'Alterar e-mail de preferรชncia: %{link}'
view: 'Ver:'
view_profile: Ver perfil
- view_status: Ver publicaรงรฃo
+ view_status: Ver toot
applications:
- created: Aplicaรงรฃo criada com sucesso
- destroyed: Aplicaรงรฃo eliminada com sucesso
- invalid_url: O URL รฉ invรกlido
- regenerate_token: Regenerar token de acesso
- token_regenerated: Token de acesso regenerado com sucesso
- warning: Cuidado com estes dados. Nรฃo partilhar com ninguรฉm!
- your_token: O teu token de acesso
+ created: Aplicativo criado com sucesso
+ destroyed: Aplicativo excluรญdo com sucesso
+ invalid_url: O link fornecido รฉ invรกlido
+ regenerate_token: Gerar cรณdigo de acesso
+ token_regenerated: Cรณdigo de acesso gerado com sucesso
+ warning: Tenha cuidado com estes dados. Nunca compartilhe com alguรฉm!
+ your_token: Seu cรณdigo de acesso
auth:
- apply_for_account: Solicitar um convite
- change_password: Palavra-passe
- checkbox_agreement_html: Concordo com as <a href="%{rules_path}" target="_blank">regras da instรขncia</a> e com os <a href="%{terms_path}" target="_blank">termos de serviรงo</a>
+ apply_for_account: Solicitar convite
+ change_password: Senha
+ checkbox_agreement_html: Concordo com <a href="%{rules_path}" target="_blank">as regras da instรขncia</a> e com <a href="%{terms_path}" target="_blank">os termos de serviรงo</a>
checkbox_agreement_without_rules_html: Concordo com os <a href="%{terms_path}" target="_blank">termos do serviรงo</a>
- delete_account: Eliminar conta
- delete_account_html: Se deseja eliminar a sua conta, pode <a href="%{path}">continuar aqui</a>. Uma confirmaรงรฃo serรก solicitada.
+ delete_account: Excluir conta
+ delete_account_html: Se vocรช deseja excluir sua conta, vocรช pode <a href="%{path}">fazer isso aqui</a>. Uma confirmaรงรฃo serรก solicitada.
description:
- prefix_invited_by_user: "@%{name} convidou-o a juntar-se a esta instรขncia do Mastodon!"
- prefix_sign_up: Inscreva-se hoje no Mastodon!
- suffix: Com uma conta, poderรก seguir pessoas, publicar atualizaรงรตes e trocar mensagens com utilizadores de qualquer instรขncia Mastodon e muito mais!
- didnt_get_confirmation: Nรฃo recebeu o email de confirmaรงรฃo?
- dont_have_your_security_key: Nรฃo tem a sua chave de seguranรงa?
- forgot_password: Esqueceste a palavra-passe?
- invalid_reset_password_token: Token de modificaรงรฃo da palavra-passe รฉ invรกlido ou expirou. Por favor, solicita um novo.
- link_to_otp: Insere um cรณdigo de duas etapas do teu telemรณvel ou um cรณdigo de recuperaรงรฃo
- link_to_webauth: Usa o teu dispositivo de chave de seguranรงa
+ prefix_invited_by_user: "@%{name} convidou vocรช para entrar nesta instรขncia Mastodon!"
+ prefix_sign_up: Crie uma conta no Mastodon hoje!
+ suffix: Com uma conta, vocรช poderรก seguir pessoas, postar atualizaรงรตes, trocar mensagens com usuรกrios de qualquer instรขncia Mastodon e muito mais!
+ didnt_get_confirmation: Nรฃo recebeu instruรงรตes de confirmaรงรฃo?
+ dont_have_your_security_key: Nรฃo estรก com a sua chave de seguranรงa?
+ forgot_password: Esqueceu a sua senha?
+ invalid_reset_password_token: O token de redefiniรงรฃo de senha รฉ invรกlido ou expirou. Solicite um novo token.
+ link_to_otp: Digite um cรณdigo de duas etapas do seu telefone ou um cรณdigo de recuperaรงรฃo
+ link_to_webauth: Use seu dispositivo de chave de seguranรงa
login: Entrar
logout: Sair
- migrate_account: Mudar para uma conta diferente
- migrate_account_html: Se deseja redirecionar esta conta para uma outra pode <a href="%{path}">configurar isso aqui</a>.
- or_log_in_with: Ou iniciar sessรฃo com
- providers:
- cas: CAS
- saml: SAML
- register: Registar
- registration_closed: "%{instance} nรฃo estรก a aceitar novos membros"
+ migrate_account: Mudar-se para outra conta
+ migrate_account_html: Se vocรช quer redirecionar essa conta para uma outra vocรช pode <a href="%{path}">configurar isso aqui</a>.
+ or_log_in_with: Ou entre com
+ register: Criar conta
+ registration_closed: "%{instance} nรฃo estรก aceitando novos membros"
resend_confirmation: Reenviar instruรงรตes de confirmaรงรฃo
- reset_password: Criar nova palavra-passe
- security: Alterar palavra-passe
- set_new_password: Editar palavra-passe
+ reset_password: Redefinir senha
+ security: Seguranรงa
+ set_new_password: Definir uma nova senha
setup:
- email_below_hint_html: Se o endereรงo de e-mail abaixo estiver incorreto, pode alterรก-lo aqui e receber um novo e-mail de confirmaรงรฃo.
- email_settings_hint_html: O e-mail de confirmaรงรฃo foi enviado para %{email}. Se esse endereรงo de e-mail nรฃo estiver correcto, pode alterรก-lo nas definiรงรตes da conta.
- title: Configuraรงรฃo
+ email_below_hint_html: Se o endereรงo de e-mail abaixo nรฃo for seu, vocรช pode alterรก-lo aqui e receber um novo e-mail de confirmaรงรฃo.
+ email_settings_hint_html: O e-mail de confirmaรงรฃo foi enviado para %{email}. Se esse endereรงo de e-mail nรฃo estiver correto, vocรช pode alterรก-lo nas configuraรงรตes da conta.
+ title: Configuraรงรตes
status:
- account_status: Estado da conta
- confirming: A aguardar que conclua a confirmaรงรฃo do e-mail.
- functional: A sua conta estรก totalmente operacional.
- pending: A sua inscriรงรฃo estรก pendente de revisรฃo pela nossa equipa. Isso pode demorar algum tempo. Receberรก um e-mail se a sua conta for aprovada.
- redirecting_to: A sua conta estรก inativa porque estรก atualmente a ser redirecionada para %{acct}.
- too_fast: Formulรกrio enviado muito rapidamente, tente novamente.
- trouble_logging_in: Problemas em iniciar sessรฃo?
+ account_status: Status da conta
+ confirming: Confirmaรงรฃo por e-mail pendente.
+ functional: Sua conta estรก totalmente operacional.
+ pending: Sua solicitaรงรฃo estรก com revisรฃo pendente por parte de nossa equipe. Vocรช receberรก um e-mail se ela for aprovada.
+ redirecting_to: Sua conta estรก inativa porque atualmente estรก redirecionando para %{acct}.
+ too_fast: O formulรกrio foi enviado muito rapidamente, tente novamente.
+ trouble_logging_in: Problemas para entrar?
use_security_key: Usar chave de seguranรงa
authorize_follow:
- already_following: Tu jรก estรกs a seguir esta conta
- already_requested: Jรก enviou anteriormente um pedido para seguir esta conta
+ already_following: Vocรช jรก segue
+ already_requested: Vocรช jรก enviou uma solicitaรงรฃo para seguir esta conta
error: Infelizmente, ocorreu um erro ao buscar a conta remota
follow: Seguir
- follow_request: 'Enviaste uma solicitaรงรฃo de seguidor para:'
- following: 'Sucesso! Agora estรกs a seguir a:'
+ follow_request: 'Vocรช mandou solicitaรงรฃo para seguir para:'
+ following: 'Sucesso! Vocรช agora estรก seguindo:'
post_follow:
- close: Ou podes simplesmente fechar esta janela.
- return: Voltar ao perfil do utilizador
+ close: Ou vocรช pode simplesmente fechar esta janela.
+ return: Mostrar o perfil do usuรกrio
web: Voltar ร  pรกgina inicial
title: Seguir %{acct}
challenge:
confirm: Continuar
- hint_html: "<strong>Dica:</strong> Nรฃo vamos pedir novamente a sua senha durante a prรณxima hora."
+ hint_html: "<strong>Dica:</strong> Nรฃo pediremos novamente sua senha pela prรณxima hora."
invalid_password: Senha invรกlida
- prompt: Confirme a sua senha para continuar
+ prompt: Confirme sua senha para continuar
+ chats:
+ errors:
+ creator_started: Nรฃo foi possรญvel aceitar esse chat jรก que vocรช o iniciou
+ blocked_constraints: Nรฃo foi possรญvel executar a solicitaรงรฃo devido a restriรงรตes bloqueadas
+ unfollowed_and_left_chat_by_user: This user has left the chat and no longer follows you
crypto:
errors:
invalid_key: nรฃo รฉ uma chave Ed25519 ou Curve25519 vรกlida
invalid_signature: nรฃo รฉ uma assinatura Ed25519 vรกlida
date:
formats:
- default: "%d %b %Y"
- with_month_name: "%d %B %Y"
+ default: "%d %b, %Y"
+ with_month_name: "%d de %b de %Y"
datetime:
distance_in_words:
- about_x_hours: "%{count}h"
- about_x_months: "%{count} meses"
- about_x_years: "%{count} anos"
- almost_x_years: "%{count} anos"
- half_a_minute: Justo agora
- less_than_x_minutes: "%{count} meses"
- less_than_x_seconds: Justo agora
- over_x_years: "%{count} anos"
- x_days: "%{count} dias"
- x_minutes: "%{count} minutos"
- x_months: "%{count} meses"
- x_seconds: "%{count} segundos"
+ about_x_months: "%{count}m"
+ about_x_years: "%{count}a"
+ almost_x_years: "%{count}a"
+ half_a_minute: Agora
+ less_than_x_seconds: Agora
+ over_x_years: "%{count}a"
+ x_minutes: "%{count}min"
+ x_months: "%{count}m"
+ x_seconds: "%{count}seg"
deletes:
- challenge_not_passed: A informaรงรฃo que introduziu nรฃo estava correta
- confirm_password: Introduza a sua palavra-passe atual para verificar a sua identidade
- confirm_username: Introduza o seu nome de utilizador para confirmar o procedimento
- proceed: Eliminar conta
- success_msg: A sua conta foi eliminada com sucesso
+ challenge_not_passed: A informaรงรฃo que vocรช inseriu nรฃo estรก correta
+ confirm_password: Digite a sua senha atual para verificar a sua identidade
+ confirm_username: Digite seu nome de usuรกrio para confirmar o procedimento
+ proceed: Excluir conta
+ success_msg: Sua conta foi excluรญda com sucesso
warning:
- before: 'Antes de continuar, por favor leia cuidadosamente estas notas:'
- caches: O conteรบdo que foi armazenado em cache por outras instรขncias pode persistir
- data_removal: As suas publicaรงรตes e outros dados serรฃo eliminados permanentemente
- email_change_html: Pode <a href="%{path}">alterar o seu endereรงo de e-mail</a> sem eliminar a sua conta
- email_contact_html: Se ainda nรฃo chegou, pode enviar um e-mail a <a href="mailto:%{email}">%{email}</a> para obter ajuda
- email_reconfirmation_html: Se nรฃo recebeu o e-mail de confirmaรงรฃo, pode <a href="%{path}">pedi-lo novamente</a>
- irreversible: Nรฃo serรก possรญvel restaurar ou reativar sua conta
- more_details_html: Para mais detalhes, leia a <a href="%{terms_path}">polรญtica de privacidade</a>.
- username_available: O seu nome de utilizador ficarรก novamente disponรญvel
- username_unavailable: O seu nome de utilizador permanecerรก indisponรญvel
+ before: 'Antes de prosseguir, por favor leia com cuidado:'
+ caches: Conteรบdo que foi armazenado em cache por outras instรขncias pode continuar a existir
+ data_removal: Seus toots e outros dados serรฃo removidos permanentemente
+ email_change_html: Vocรช pode <a href="%{path}">alterar seu endereรงo de e-mail</a> sem excluir sua conta
+ email_contact_html: Se vocรช ainda nรฃo recebeu, vocรช pode enviar um e-mail pedindo ajuda para <a href="mailto:%{email}">%{email}</a>
+ email_reconfirmation_html: Se vocรช nรฃo estรก recebendo o e-mail de confirmaรงรฃo, vocรช pode <a href="%{path}">solicitรก-lo novamente</a>
+ irreversible: Vocรช nรฃo conseguirรก restaurar ou reativar a sua conta
+ more_details_html: Para mais detalhes, consulte a <a href="%{terms_path}">Polรญtica de Privacidade</a>.
+ username_available: Seu nome de usuรกrio ficarรก disponรญvel novamente
+ username_unavailable: Seu nome de usuรกrio permanecerรก indisponรญvel
directories:
- directory: Dirรฉtorio de perfil
- explanation: Descobre utilizadores com base nos seus interesses
- explore_mastodon: Explorar %{title}
+ directory: Diretรณrio de perfis
+ explanation: Descobrir usuรกrios baseado em seus interesses
+ explore_mastodon: Explore o %{title}
domain_validator:
invalid_domain: nรฃo รฉ um nome de domรญnio vรกlido
errors:
- '400': O pedido que submeteu foi invรกlido ou mal formulado.
- '403': Nรฃo tens a permissรฃo necessรกria para ver esta pรกgina.
- '404': A pรกgina que estรกs a procurar nรฃo existe.
+ '400': A solicitaรงรฃo enviada รฉ invรกlida ou incorreta.
+ '403': Vocรช nรฃo tem permissรฃo para ver esta pรกgina.
+ '404': A pรกgina pela qual vocรช estรก procurando nรฃo existe.
'406': Esta pรกgina nรฃo estรก disponรญvel no formato solicitado.
- '410': A pรกgina que estรกs a procurar nรฃo existe mais.
+ '410': A pรกgina que vocรช procura nรฃo existe mais.
'422':
- content: "A verificaรงรฃo de seguranรงa falhou. \nDesativaste o uso de cookies?"
- title: A verificaรงรฃo de seguranรงa falhou
- '429': Desacelerado
+ content: Falha na verificaรงรฃo de seguranรงa. Vocรช estรก bloqueando cookies?
+ title: Falha na verificaรงรฃo de seguranรงa
+ '429': Excesso de solicitaรงรตes
'500':
- content: Desculpe, mas algo correu mal.
- title: Esta pรกgina nรฃo estรก correta
- '503': A pรกgina nรฃo pรดde ser apresentada devido a uma falha temporรกria do servidor.
- noscript_html: Para usar o aplicativo web do Mastodon, por favor ativa o JavaScript. Alternativamente, experimenta um dos <a href="%{apps_path}">apps nativos</a> para o Mastodon na sua plataforma.
+ content: Desculpe, algo deu errado por aqui.
+ title: Esta pรกgina nรฃo estรก certa
+ '503': A pรกgina nรฃo pรดde ser carregada devido a uma falha temporรกria do servidor.
+ noscript_html: Para usar o aplicativo web do Mastodon, por favor ative o JavaScript. Ou, se quiser, experimente um dos <a href="%{apps_path}">aplicativos nativos</a> para o Mastodon em sua plataforma.
+ api:
+ '401': Esse mรฉtodo requer um usuรกrio autenticado
+ '403': Essa aรงรฃo nรฃo รฉ permitida
+ '404': Registro nรฃo encontrado
+ '503': Ocorreu um problema temporรกrio ao atender sua solicitaรงรฃo. Tente novamente.
+ assertion: Nรฃo foi possรญvel verificar sua afirmaรงรฃo
+ attestation: Nรฃo foi possรญvel verificar seu atestado
+ data_fetch: Nรฃo foi possรญvel resgatar os dados remotos
+ duplicate: Registro duplicado
+ login_disabled: No momento seu login estรก desativado
+ login_pending: No momento seu login estรก aguardando aprovaรงรฃo
+ missing_email: Estรก faltando um endereรงo de e-mail confirmado para o seu login
+ ssl: Nรฃo foi possรญvel verificar o Certificado SSL remoto
+ outside_scopes: Essa aรงรฃo estรก fora do escopo autorizado
+ unauthorized: Nรฃo autorizado
existing_username_validator:
- not_found: nรฃo foi possรญvel encontrar um utilizador local com esse nome
+ not_found: nรฃo foi possรญvel encontrar um usuรกrio local com esse nome de usuรกrio
not_found_multiple: nรฃo foi possรญvel encontrar %{usernames}
exports:
archive_takeout:
date: Data
- download: Descarregar o teu arquivo
- hint_html: Pode pedir um arquivo das suas <strong>publicaรงรตes e ficheiros de media carregados</strong>. Os dados no ficheiro exportado estarรฃo no formato ActivityPub, que pode ser lido com qualquer software compatรญvel. Pode solicitar um arquivo a cada 7 dias.
- in_progress: A compilar o seu arquivo...
- request: Pede o teu arquivo
+ download: Baixe o seu arquivo
+ hint_html: Vocรช pode pedir um arquivo dos seus <strong>toots e mรญdias enviadas</strong>. Os dados exportados estarรฃo no formato ActivityPub, que podem ser lidos por qualquer software compatรญvel. Vocรช pode pedir um arquivo a cada 7 dias.
+ in_progress: Preparando o seu arquivo...
+ request: Solicitar o seu arquivo
size: Tamanho
- blocks: Bloqueaste
- bookmarks: Itens Salvos
- csv: CSV
+ blocks: Vocรช bloqueou
+ bookmarks: Marcadores
domain_blocks: Bloqueios de domรญnio
lists: Listas
- mutes: Tens em silรชncio
- storage: Armazenamento de media
+ mutes: Vocรช silenciou
+ storage: Armazenamento de mรญdia
featured_tags:
- add_new: Adicionar nova
+ add_new: Adicionar hashtag
errors:
- limit: Jรก atingiste o limite mรกximo de hashtags
- hint_html: "<strong>O que sรฃo hashtags em destaque?</strong> Elas sรฃo exibidas de forma bem visรญvel no seu perfil pรบblico e permitem que as pessoas consultem as suas publicaรงรตes pรบblicas especificamente sob essas hashtags. Sรฃo uma รณtima ferramenta para manter o controlo de trabalhos criativos ou projetos de longo prazo."
+ limit: Vocรช atingiu o limite de hashtags em destaque
+ hint_html: "<strong>O que sรฃo hashtags em destaque?</strong> Elas sรฃo mostradas no seu perfil pรบblico e permitem que as pessoas acessem seus toots pรบblicos que contenham especificamente essas hashtags. Sรฃo uma excelente ferramenta para acompanhar os trabalhos criativos ou os projetos de longo prazo."
filters:
contexts:
account: Perfis
- home: Cronologia inicial
+ home: Pรกgina inicial
notifications: Notificaรงรตes
- public: Cronologias pรบblicas
- thread: Conversaรงรตes
+ public: Linhas pรบblicas
+ thread: Conversas
edit:
- title: Editar filtros
+ title: Editar filtro
errors:
- invalid_context: Invรกlido ou nenhum contexto fornecido
- invalid_irreversible: Filtragem irreversรญvel sรณ funciona no contexto das notificaรงรตes ou do inรญcio
+ invalid_context: Contexto invรกlido ou nenhum contexto informado
+ invalid_irreversible: O filtro irreversรญvel sรณ funciona com os contextos pรกgina inicial e notificaรงรตes
index:
- delete: Eliminar
- empty: Nรฃo tem filtros.
+ delete: Remover
+ empty: Sem filtros.
title: Filtros
new:
- title: Adicionar novo filtro
+ title: Adicionar filtro
footer:
- developers: Responsรกveis pelo desenvolvimento
+ developers: Desenvolvedores
more: Maisโ€ฆ
resources: Recursos
- trending_now: Tendรชncias atuais
+ trending_now: Em alta no momento
generic:
all: Tudo
- changes_saved_msg: Alteraรงรตes guardadas!
+ changes_saved_msg: Alteraรงรตes salvas com sucesso!
copy: Copiar
- delete: Eliminar
+ delete: Excluir
no_batch_actions_available: Nenhuma aรงรฃo em lote disponรญvel nesta pรกgina
order_by: Ordenar por
- save_changes: Guardar alteraรงรตes
+ save_changes: Salvar alteraรงรตes
validation_errors:
- one: Algo nรฃo estรก correcto. Por favor vรช o erro abaixo
- other: Algo nรฃo estรก correto. Por favor vรช os %{count} erros abaixo
+ one: Algo errado nรฃo estรก certo! Por favor, analise o erro abaixo
+ other: Algo errado nรฃo estรก certo! Por favor, analise os %{count} erros abaixo
+ groups:
+ errors:
+ pending_request_conflict: O proprietรกrio ou administrador do grupo jรก tomou medidas sobre esta solicitaรงรฃo.
+ too_many_admins: Vocรช pode atribuir atรฉ %{count} administradores para o grupo.
html_validator:
- invalid_markup: 'contรฉm marcaรงรฃo HTML invรกlida: %{error}'
+ invalid_markup: 'contรฉm HTML invรกlido: %{error}'
identity_proofs:
active: Ativo
authorize: Sim, autorizar
- authorize_connection_prompt: Autorizar esta conexรฃo criptogrรกfica?
+ authorize_connection_prompt: Autorizar essa conexรฃo criptogrรกfica?
errors:
- failed: A conexรฃo criptogrรกfica falhou. Por favor, tente novamente a partir de %{provider}.
+ failed: Falha na conexรฃo criptogrรกfica. Por favor, tente novamente a partir de %{provider}.
keybase:
- invalid_token: Os tokens Keybase sรฃo hashes de assinaturas e devem conter 66 caracteres hexadecimais
- verification_failed: O Keybase nรฃo reconhece este token como uma assinatura do utilizador do Keybase %{kb_username}. Por favor, tente novamente a partir do Keybase.
- wrong_user: Nรฃo รฉ possรญvel criar um comprovativo para %{proving} enquanto estiver conetado como %{current}. Inicie sessรฃo como %{proving} e tente novamente.
- explanation_html: Aqui pode conetar criptograficamente as suas outras identidades, tais como um perfil Keybase. Isto permite que outras pessoas lhe enviem mensagens encriptadas e confiar em conteรบdo que vocรช lhes envia.
- i_am_html: Sou %{username} em %{service}.
+ invalid_token: Tokens keybase sรฃo hashes de assinatura e devem conter 66 caracteres hexa
+ verification_failed: Keybase nรฃo reconhece esse token como uma assinatura do usuรกrio keybase %{kb_username}. Por favor, tente novamente a partir do Keybase.
+ wrong_user: Nรฃo foi possรญvel criar uma prova para %{proving} como %{current}. Entre como %{proving} e tente novamente.
+ explanation_html: Vocรช pode conectar criptograficamente suas outras identidades, tais quais seu perfil Keybase. Isso permite outras pessoas de lhe enviarem mensagens criptografadas e confiar no conteรบdo que vocรช as envia.
+ i_am_html: Eu sou %{username} em %{service}.
identity: Identidade
inactive: Inativo
- publicize_checkbox: 'E publique isso:'
- publicize_toot: 'Estรก comprovado! Eu sou %{username} em %{service}: %{url}'
- remove: Remover comprovatido da conta
- removed: Comprovativo removido da conta com sucesso
- status: Estado da verificaรงรฃo
+ publicize_checkbox: 'E toote isso:'
+ publicize_toot: 'Estรก provado! Eu sou %{username} no %{service}: %{url}'
+ remove: Remover prova da conta
+ removed: Prova removida da conta com sucesso
+ status: Status da verificaรงรฃo
view_proof: Ver prova
imports:
- errors:
- over_rows_processing_limit: contรฉm mais de %{count} linhas
modes:
merge: Juntar
- merge_long: Manter os registos existentes e adicionar novos registos
- overwrite: Escrever por cima
- overwrite_long: Substituir os registos atuais pelos novos
- preface: Podes importar dados que tenhas exportado de outra instรขncia, como a lista de pessoas que segues ou bloqueadas.
- success: Os teus dados foram enviados com sucesso e serรฃo processados em breve
+ merge_long: Manter os registros existentes e adicionar novos
+ overwrite: Sobrescrever
+ overwrite_long: Substituir os registros atuais com os novos
+ preface: Vocรช pode importar dados que vocรช exportou de outra instรขncia, como a lista de pessoas que vocรช segue ou bloqueou.
+ success: Os seus dados foram enviados com sucesso e serรฃo processados em instantes
types:
blocking: Lista de bloqueio
- bookmarks: Itens salvos
+ bookmarks: Marcadores
domain_blocking: Lista de domรญnios bloqueados
- following: Lista de pessoas que estรกs a seguir
- muting: Lista de utilizadores silenciados
+ following: Pessoas que vocรช segue
+ muting: Lista de silenciados
upload: Enviar
in_memoriam_html: Em memรณria.
invites:
@@ -992,143 +932,231 @@
'604800': 1 semana
'86400': 1 dia
expires_in_prompt: Nunca
- generate: Gerar
- invited_by: 'Tu foste convidado por:'
+ generate: Gerar convite
+ invited_by: 'Vocรช recebeu convite de:'
max_uses:
one: 1 uso
other: "%{count} usos"
max_uses_prompt: Sem limite
- prompt: Gerar e partilhar ligaรงรตes com outras pessoas para permitir acesso a essa instรขncia
+ prompt: Gere e compartilhe links para permitir acesso a essa instรขncia
table:
- expires_at: Expira
+ expires_at: Expira em
uses: Usos
title: Convidar pessoas
lists:
errors:
- limit: Nรบmero mรกximo de listas alcanรงado
+ limit: Vocรช atingiu o mรกximo de listas
media_attachments:
validations:
- images_and_video: Nรฃo รฉ possรญvel anexar um vรญdeo a uma publicaรงรฃo que jรก contรฉm imagens
- not_ready: Nรฃo รฉ possรญvel anexar arquivos que nรฃo terminaram de ser processados. Tente novamente daqui a pouco!
+ images_and_video: Nรฃo รฉ possรญvel anexar um vรญdeo a uma postagem que jรก contรฉm imagens
+ not_ready: Nรฃo รฉ possรญvel anexar arquivos cujo processamento ainda nรฃo foi concluรญdo. Tente novamente daqui a pouco!
too_many: Nรฃo รฉ possรญvel anexar mais de 4 arquivos
migrations:
- acct: username@domain da nova conta
+ acct: Mudou-se para
cancel: Cancelar redirecionamento
- cancel_explanation: Cancelar o redirecionamento irรก reativar a sua conta atual, mas nรฃo trarรก de volta os seguidores que foram migrados para essa conta.
- cancelled_msg: Cancelou com sucesso o redireccionamento.
+ cancel_explanation: Cancelar o redirecionamento reativarรก a sua conta atual, mas nรฃo trarรก de volta os seguidores que nรฃo foram migrados para aquela conta.
+ cancelled_msg: Redirecionamento cancelado com sucesso.
errors:
- already_moved: รฉ a mesma conta para a qual jรก migrou
- missing_also_known_as: nรฃo estรก a referenciar esta conta
- move_to_self: nรฃo pode ser conta atual
- not_found: nรฃo pode ser encontrado
+ already_moved: รฉ a mesma conta que vocรช migrou
+ missing_also_known_as: nรฃo estรก referenciando esta conta
+ move_to_self: nรฃo pode ser a conta atual
+ not_found: nรฃo pรดde ser encontrado
on_cooldown: Vocรช estรก no perรญodo de espera
- followers_count: Seguidores no momento da migraรงรฃo
- incoming_migrations: A migrar de uma conta diferente
- incoming_migrations_html: Para migrar de outra conta para esta, primeiro vocรช precisa <a href="%{path}">criar um pseudรณnimo</a>.
- moved_msg: A sua conta estรก agora a ser redireccionada para %{acct} e os seus seguidores estรฃo a ser transferidos.
- not_redirecting: A sua conta nรฃo estรก atualmente a ser redirecionada para nenhuma outra conta.
- on_cooldown: Migrou recentemente a sua conta. Esta funรงรฃo ficarรก disponรญvel novamente em %{count} dias.
- past_migrations: Migraรงรตes anteriores
+ followers_count: Seguidores no momento da mudanรงa
+ incoming_migrations: Migrando de outra conta
+ incoming_migrations_html: Para mover de outra conta para esta, vocรช precisa <a href="%{path}">criar um alias</a>.
+ moved_msg: Agora sua conta estรก redirecionando para %{acct} e seus seguidores estรฃo sendo movidos.
+ not_redirecting: Sua conta nรฃo estรก redirecionando para nenhuma outra conta atualmente.
+ on_cooldown: Vocรช migrou recentemente sua conta. Esta funรงรฃo ficarรก disponรญvel novamente em %{count} dias.
+ past_migrations: Migraรงรตes passadas
proceed_with_move: Migrar seguidores
- redirected_msg: A sua conta estรก agora a ser redireccionada para %{acct}.
- redirecting_to: A sua conta estรก a ser redireccionada para %{acct}.
+ redirected_msg: Agora sua conta estรก redirecionando para %{acct}.
+ redirecting_to: Sua conta estรก redirecionando para %{acct}.
set_redirect: Definir redirecionamento
warning:
backreference_required: A nova conta deve primeiro ser configurada para que esta seja referenciada
- before: 'Antes de continuar, leia cuidadosamente estas notas:'
- cooldown: Apรณs a migraรงรฃo, hรก um perรญodo de tempo de espera durante o qual nรฃo poderรก voltar a migrar
- disabled_account: Posteriormente, a sua conta atual nรฃo serรก totalmente utilizรกvel. No entanto, continuarรก a ter acesso ร  exportaรงรฃo de dados, bem como ร  reativaรงรฃo.
- followers: Esta aรงรฃo irรก migrar todos os seguidores da conta atual para a nova conta
- only_redirect_html: Em alternativa, pode <a href="%{path}">apenas colocar um redireccionamento no seu perfil</a>.
- other_data: Nenhum outro dado serรก migrado automaticamente
- redirect: O perfil da sua conta atual serรก atualizado com um aviso de redirecionamento e serรก excluรญdo das pesquisas
+ before: 'Antes de prosseguir, por favor leia com cuidado:'
+ cooldown: Depois de se mudar, hรก um perรญodo de espera para poder efetuar uma nova mudanรงa
+ disabled_account: Sua conta nรฃo estarรก totalmente funcional ao tรฉrmino deste processo. Entretanto, vocรช terรก acesso ร  exportaรงรฃo de dados bem como ร  reativaรงรฃo.
+ followers: Esta aรงรฃo moverรก todos os seguidores da conta atual para a nova conta
+ only_redirect_html: Alternativamente, vocรช pode <a href="%{path}">apenas colocar um redirecionamento no seu perfil</a>.
+ other_data: Nenhum outro dado serรก movido automaticamente
+ redirect: O perfil atual da sua conta serรก atualizado com um aviso de redirecionamento e tambรฉm serรก excluรญdo das pesquisas
moderation:
title: Moderaรงรฃo
move_handler:
- carry_blocks_over_text: Este utilizador migrou de %{acct}, que vocรช tinha bloqueado.
- carry_mutes_over_text: Este utilizador migrou de %{acct}, que vocรช tinha silenciado.
- copy_account_note_text: 'Este utilizador migrou de %{acct}, aqui estรฃo as suas notas anteriores sobre ele:'
+ carry_blocks_over_text: Este usuรกrio mudou de %{acct}, que vocรช havia bloqueado.
+ carry_mutes_over_text: Este usuรกrio mudou de %{acct}, que vocรช havia silenciado.
+ copy_account_note_text: 'Este usuรกrio saiu de %{acct}, aqui estรฃo suas notas anteriores sobre ele:'
notification_mailer:
+ chat:
+ subject: "Nova mensagem de %{name}"
+ subject_android: "lhe enviou uma mensagem"
+ sent_message: "Nova mensagem"
digest:
action: Ver todas as notificaรงรตes
- body: Aqui tens um breve resumo do que perdeste desde o รบltimo acesso a %{since}
- mention: "%{name} mencionou-te em:"
+ body: Aqui estรก um breve resumo das mensagens que vocรช nรฃo viu desde seu รบltimo acesso em %{since}
+ mention: "%{name} mencionou vocรช em:"
new_followers_summary:
- one: Tens um novo seguidor! Boa!
- other: Tens %{count} novos seguidores! Fantรกstico!
+ one: Alรฉm disso, enquanto estava fora vocรช ganhou um novo seguidor! Oba!
+ other: Alรฉm disso, enquanto estava fora vocรช ganhou %{count} novos seguidores! Incrรญvel!
subject:
- one: "1 nova notificaรงรฃo desde o รบltimo acesso \U0001F418"
- other: "%{count} novas notificaรงรตes desde o รบltimo acesso \U0001F418"
- title: Enquanto estiveste ausenteโ€ฆ
+ one: "1 nova notificaรงรฃo desde seu รบltimo acesso \U0001F418"
+ other: "%{count} novas notificaรงรตes desde seu รบltimo acesso \U0001F418"
+ subject_android:
+ one: "1 nova notificaรงรฃo desde seu รบltimo acesso \U0001F418"
+ other: "%{count} novas notificaรงรตes desde seu รบltimo acesso \U0001F418"
+ title: Na sua ausรชncia...
favourite:
- body: 'O teu post foi adicionado aos favoritos por %{name}:'
- subject: "%{name} adicionou o teu post aos favoritos"
+ body: "Sua postagem foi curtida por %{name}:"
+ subject: "%{name} curtiu sua postagem"
+ subject_android: "gostou da sua Truth"
title: Novo favorito
+ favourite_group:
+ subject: "%{name} + %{count_others} %{actor} curtiram sua Truth"
+ subject_android: "%{name} e mais %{count_others} pessoas gostaram da sua Truth"
+ group_favourite:
+ body: "Sua postagem foi curtida por %{name}:"
+ subject: "%{name} curtiu sua postagem"
+ subject_android: "gostou da sua Truth em %{group}"
+ title: Novo favorito
+ group_favourite_group:
+ subject: "%{name} + %{count_others} %{actor} curtiram sua Truth"
+ subject_android: "%{name} e mais %{count_others} pessoas gostaram da sua Truth em %{group}"
follow:
- body: "%{name} รฉ teu seguidor!"
- subject: "%{name} comeรงou a seguir-te"
+ body: "%{name} estรก seguindo vocรช!"
+ subject: "%{name} estรก seguindo vocรช"
+ subject_android: "estรก te seguindo agora"
title: Novo seguidor
+ follow_group:
+ subject: "%{name} + %{count_others} %{actor} seguiram vocรช"
+ subject_android: "%{name} e %{count_others} outros seguiram vocรช"
follow_request:
- action: Gerir pedidos de seguidores
- body: "%{name} solicitou autorizaรงรฃo para te seguir"
- subject: 'Seguidor pendente: %{name}'
+ action: Gerenciar solicitaรงรตes de seguidores
+ body: "%{name} pediu para seguir vocรช"
+ subject: 'Seguidor aguardando: %{name}'
+ subject_android: 'pediu para te seguir'
title: Nova solicitaรงรฃo de seguidor
mention:
action: Responder
- body: 'Foste mencionado por %{name}:'
- subject: "%{name} mencionou-te"
+ body: "Vocรช foi mencionado por %{name} em:"
+ subject: "Vocรช foi mencionado por %{name}"
+ subject_android: "mencionou vocรช em uma Truth"
title: Nova menรงรฃo
+ mention_group:
+ subject: "%{name} + %{count_others} %{actor} mencionaram vocรช"
+ subject_android: "%{name} e %{count_others} outros mencionaram vocรช"
+ group_mention:
+ action: Responder
+ body: "Vocรช foi mencionado por %{name} em:"
+ subject: "Vocรช foi mencionado por %{name}"
+ subject_android: "mencionou vocรช em %{group}"
+ title: Nova menรงรฃo
+ group_mention_group:
+ subject: "%{name} + %{count_others} %{actor} mencionaram vocรช"
+ subject_android: "%{name} e %{count_others} outros mencionaram vocรช"
poll:
- subject: Uma votaรงรฃo realizada por %{name} terminou
+ subject: Uma enquete postada por %{name} foi encerrada
+ subject_android: "concluiu uma enquete"
reblog:
- body: 'O teu post foi partilhado por %{name}:'
- subject: "%{name} partilhou o teu post"
- title: Nova partilha
+ body: "Sua Truth foi RePostada por %{name}:"
+ subject: "%{name} RePostou sua Truth"
+ subject_android: "ReTruthed sua Truth"
+ title: Nova RePostagem
+ reblog_group:
+ subject: "%{name} + %{count_others} %{actor} RePostaram sua Truth"
+ subject_android: "%{name} e mais %{count_others} outros ReTruthed sua Truth"
+ group_reblog:
+ body: "Sua Truth foi RePostada por %{name}:"
+ subject: "%{name} RePostou sua Truth"
+ subject_android: "ReTruthed sua Truth de %{group}"
+ title: Nova RePostagem
+ group_reblog_group:
+ subject: "%{name} + %{count_others} %{actor} RePostaram sua Truth"
+ subject_android: "%{name} e %{count_others} outros ReTruthed a sua Truth de grupo"
+ group_request:
+ body: '%{name} wants to join your group:'
+ subject: "%{name} wants to join your group"
+ subject_android: "vocรช tem uma nova solicitaรงรฃo de membro"
+ title: Group Join Request
status:
- subject: "%{name} acabou de publicar"
+ subject: "%{name} acabou de postar"
+ subject_android: "acabou de postar"
+ group_promoted:
+ body: You are now an admin for %{group}
+ subject: You are now an admin for %{group}
+ subject_android: "agora vocรช รฉ um administrador"
+ title: You're an admin
+ group_demoted:
+ body: You are no longer an admin for %{group}
+ subject: You are no longer an admin for %{group}
+ subject_android: "vocรช nรฃo รฉ mais administrador"
+ title: You're no longer an admin
+ user_approved:
+ edit_profile_action: Ir para o perfil
+ edit_profile_step: Para terminar, nรฃo deixe de personalizar seu perfil carregando um avatar, um tรญtulo, mudando seu nome escolhido e muito mais. Se quiser avaliar novos seguidores antes de aprovรก-los, vocรช pode bloquear sua conta.
+ explanation: Estamos super empolgados com sua chegada ร  nossa comunidade de Buscadores da Verdade. Acreditamos na liberdade de expressรฃo e incentivamos todos os pontos de vista, jรก que nรฃo discriminamos ninguรฉm devido a uma ideologia polรญtica.
+ extra_step: Alรฉm disso, gostarรญamos de lembrar que somos uma plataforma nova e ainda estamos corrigindo vรกrios bugs na nossa tecnologia. Podemos garantir que estamos trabalhando duro para aprimorar tudo o mais rรกpido possรญvel! Obrigado!
+ final_action: Comece a postar
+ final_step: 'Comece a postar! Mesmo sem nenhum seguidor suas postagens pรบblicas podem ser vistas por outras pessoas, por exemplo, na timeline local e nas hashtags. Vocรช pode querer se apresentar com a hashtag #apresentaรงรตes.'
+ full_handle: Seu identificador completo (handle)
+ full_handle_hint: ร‰ o que vocรช quer dizer aos seus amigos para que possam seguir vocรช ou lhe enviar mensagens de outro servidor.
+ review_preferences_action: Mudar preferรชncias
+ review_preferences_step: Nรฃo deixe de configurar suas preferรชncias, como os e-mails que quer receber ou o nรญvel de privacidade padrรฃo para as suas postagens. Se nรฃo costuma sentir enjoo vocรช pode optar por reproduzir GIFs automaticamente.
+ subject: "Sua espera terminou! Toque aqui para comeรงar a usar o Truth Social."
+ subject_android: "Sua espera terminou! Toque aqui para comeรงar a usar o Truth Social."
+ web:
+ subject: "Bem-vindo(a) ao Truth, onde a Verdade impera"
+ tip_federated_timeline: A timeline federada รฉ uma ampla visรฃo ao vivo da rede do Truth. Mas sรณ inclui as pessoas que seus vizinhos estรฃo seguindo, entรฃo nรฃo รฉ uma visรฃo completa.
+ tip_following: Vocรช segue os administradores do seu servidor por padrรฃo Para encontrar mais gente interessante, verifique sua timeline local e federada.
+ tip_local_timeline: A timeline local รฉ uma visรฃo ao vivo das pessoas em %{instance}. Sรฃo seus vizinhos de porta!
+ tip_mobile_webapp: Se o seu navegador mรณvel sugerir que vocรช adicione o Truth ร  sua tela inicial, vocรช poderรก receber notificaรงรตes por push. O Truth funciona de muitas maneiras como um aplicativo nativo!
+ tips: Dicas
+ title: Bem-vindo(a) ao Truth Social, %{name}!
+ verify_sms_prompt:
+ subject: Sua conta jรก estรก ativa no Truth Social! Atualize para a versรฃo mais recente e entre para a nossa turma!
+ subject_android: Sua conta jรก estรก ativa no Truth Social! Atualize para a versรฃo mais recente e entre para a nossa turma!
notifications:
email_events: Eventos para notificaรงรตes por e-mail
- email_events_hint: 'Selecione os eventos para os quais deseja receber notificaรงรตes:'
- other_settings: Outras opรงรตes de notificaรงรตes
+ email_events_hint: 'Selecione os eventos que deseja receber notificaรงรตes:'
+ other_settings: Outras opรงรตes para notificaรงรตes
number:
human:
decimal_units:
- format: "%n%u"
units:
- billion: MM
- million: M
- quadrillion: Q
- thousand: mil
- trillion: T
+ billion: BI
+ million: MI
+ quadrillion: QUA
+ thousand: MIL
+ trillion: TRI
otp_authentication:
- code_hint: Introduz o cรณdigo gerado pela tua aplicaรงรฃo de autenticaรงรฃo para confirmar
- description_html: Se ativar a <strong>autenticaรงรฃo em duas etapas</strong>, para entrar na sua conta terรก de ter consigo o seu telefone, que vai gerar os tokens necessรกrios ร  validaรงรฃo do seu acesso.
- enable: Ativar
- instructions_html: "<strong>Digitalize este cรณdigo QR no Google Authenticator ou num aplicativo TOTP similar no seu telefone</strong>. A partir de agora, esse aplicativo irรก gerar tokens que vocรช terรก que introduzir para aceder ร  sua conta."
- manual_instructions: 'Se vocรช nรฃo consegue digitalizar o cรณdigo QR e precisa introduzi-lo manualmente, aqui estรก o cรณdigo em texto:'
+ code_hint: Digite o cรณdigo gerado pelo seu aplicativo autenticador para confirmar
+ description_html: Se vocรช habilitar a <strong>autenticaรงรฃo de dois fatores</strong> usando um aplicativo autenticador, o login exigirรก que vocรช esteja com o seu telefone, que gerarรก tokens para vocรช entrar.
+ enable: Habilitar
+ instructions_html: "<strong>Escaneie este cรณdigo QR no Google Authenticator ou em um aplicativo TOTP similar no seu telefone</strong>. A partir de agora, esse aplicativo irรก gerar tokens que vocรช terรก que digitar ao fazer login."
+ manual_instructions: 'Se vocรช nรฃo pode escanear o cรณdigo QR e precisa digitรก-lo manualmente, aqui estรก o segredo em texto:'
setup: Configurar
- wrong_code: O cรณdigo introduzido รฉ invรกlido! A hora do servidor e a hora do dispositivo estรฃo corretos?
+ wrong_code: O cรณdigo que vocรช inseriu รฉ invรกlido!
pagination:
- newer: Mais nova
- next: Seguinte
- older: Mais velha
+ newer: Mais novo
+ next: Prรณximo
+ older: Mais antigo
prev: Anterior
- truncate: "&hellip;"
polls:
errors:
- already_voted: Tu jรก votaste nesta sondagem
+ already_voted: Enquete votada
duplicate_options: contรฉm itens duplicados
- duration_too_long: estรก demasiado ร  frente no futuro
- duration_too_short: รฉ demasiado cedo
- expired: A sondagem jรก terminou
- invalid_choice: A opรงรฃo de voto escolhida nรฃo existe
- over_character_limit: nรฃo pode ter mais do que %{max} caracteres cada um
- too_few_options: tem de ter mais do que um item
- too_many_options: nรฃo pode conter mais do que %{max} itens
+ duration_too_long: รฉ muito longe no futuro
+ duration_too_short: รฉ curto demais
+ expired: A enquete jรก terminou
+ invalid_choice: Opรงรฃo invรกlida
+ over_character_limit: nรฃo pode ter mais que %{max} caracteres em cada
+ too_few_options: deve ter mais que um item
+ too_many_options: nรฃo pode ter mais que %{max} itens
preferences:
other: Outro
posting_defaults: Padrรตes de publicaรงรฃo
- public_timelines: Cronologias pรบblicas
+ public_timelines: Linhas pรบblicas
reactions:
errors:
limit_reached: Limite de reaรงรตes diferentes atingido
@@ -1136,9 +1164,9 @@
relationships:
activity: Atividade da conta
dormant: Inativo
- follow_selected_followers: Seguir seguidores selecionados
+ follow_selected_followers: Seguir os seguidores selecionados
followers: Seguidores
- following: A seguir
+ following: Seguindo
invited: Convidado
last_active: รšltima atividade
most_recent: Mais recente
@@ -1147,36 +1175,36 @@
primary: Primรกrio
relationship: Relaรงรฃo
remove_selected_domains: Remover todos os seguidores dos domรญnios selecionados
- remove_selected_followers: Remover seguidores selecionados
- remove_selected_follows: Deixar de seguir os utilizadores selecionados
- status: Estado da conta
+ remove_selected_followers: Remover os seguidores selecionados
+ remove_selected_follows: Deixar de seguir usuรกrios selecionados
+ status: Status da conta
remote_follow:
- acct: Introduza o seu utilizador@domรญnio do qual quer seguir
- missing_resource: Nรฃo foi possรญvel achar a URL de redirecionamento para sua conta
- no_account_html: Nรฃo tens uma conta? Tu podes <a href='%{sign_up_path}' target='_blank'> aderir aqui</a>
- proceed: Prossiga para seguir
- prompt: 'Vocรช vai seguir:'
- reason_html: "<strong> Porque รฉ este passo necessรกrio?</strong> <code>%{instance}</code> pode nรฃo ser a instรขncia onde vocรช estรก registado. Por isso, precisamos de o redirecionar para a sua instรขncia de origem em primeiro lugar."
+ acct: Digite o seu usuรกrio@domรญnio para continuar
+ missing_resource: Nรฃo foi possรญvel encontrar o link de redirecionamento para sua conta
+ no_account_html: Nรฃo tem uma conta? Vocรช pode <a href='%{sign_up_path}' target='_blank'>criar uma aqui</a>
+ proceed: Continue para seguir
+ prompt: 'Vocรช seguirรก:'
+ reason_html: "<strong>Por que esse passo รฉ necessรกrio?</strong> <code>%{instance}</code> pode nรฃo ser a instรขncia onde vocรช se hospedou, entรฃo precisamos redirecionar vocรช para a sua instรขncia primeiro."
remote_interaction:
favourite:
- proceed: Prosseguir para os favoritos
- prompt: 'Queres favoritar esta publicaรงรฃo:'
+ proceed: Continue para favoritar
+ prompt: 'Vocรช favoritarรก este toot:'
reblog:
- proceed: Prosseguir com partilha
- prompt: 'Queres partilhar esta publicaรงรฃo:'
+ proceed: Continue para dar boost
+ prompt: 'Vocรช darรก boost neste toot:'
reply:
- proceed: Prosseguir com resposta
- prompt: 'Queres responder a esta publicaรงรฃo:'
+ proceed: Continue para responder
+ prompt: 'Vocรช responderรก este toot:'
scheduled_statuses:
- over_daily_limit: Excedeste o limite de %{limit} publicaรงรตes agendadas para esse dia
- over_total_limit: Tu excedeste o limite de %{limit} publicaรงรตes agendadas
- too_soon: A data de agendamento tem de ser futura
+ over_daily_limit: Vocรช excedeu o limite de %{limit} toots agendados para esse dia
+ over_total_limit: Vocรช excedeu o limite de %{limit} toots agendados
+ too_soon: A data agendada precisa ser no futuro
sessions:
activity: รšltima atividade
browser: Navegador
browsers:
alipay: Alipay
- blackberry: Blackberry
+ blackberry: BlackBerry
chrome: Chrome
edge: Microsoft Edge
electron: Electron
@@ -1186,7 +1214,7 @@
micro_messenger: MicroMessenger
nokia: Navegador Nokia S40 Ovi
opera: Opera
- otter: Lontra
+ otter: Otter
phantom_js: PhantomJS
qq: QQ Browser
safari: Safari
@@ -1194,36 +1222,28 @@
weibo: Weibo
current_session: Sessรฃo atual
description: "%{browser} em %{platform}"
- explanation: Estes sรฃo os navegadores que estรฃo conectados com a tua conta do Mastodon.
- ip: IP
+ explanation: Estes sรฃo os navegadores que estรฃo conectados com a sua conta Mastodon.
platforms:
- adobe_air: Adobe Air
- android: Android
- blackberry: Blackberry
- chrome_os: ChromeOS
- firefox_os: SO Firefox
- ios: iOS
- linux: Linux
- mac: Mac
+ blackberry: BlackBerry
+ mac: MacOS
other: Plataforma desconhecida
- windows: Windows
windows_mobile: Windows Mobile
windows_phone: Windows Phone
- revoke: Revogar
- revoke_success: Sessรฃo revogada com sucesso
+ revoke: Fechar
+ revoke_success: A sessรฃo foi revogada com sucesso
title: Sessรตes
settings:
account: Conta
- account_settings: Definiรงรตes da conta
- aliases: Pseudรณnimos da conta
- appearance: Aspecto
+ account_settings: Configuraรงรตes da conta
+ aliases: Alias da conta
+ appearance: Aparรชncia
authorized_apps: Aplicativos autorizados
- back: Voltar ao Mastodon
- delete: Eliminaรงรฃo da conta
+ back: Voltar para o Mastodon
+ delete: Exclusรฃo de conta
development: Desenvolvimento
edit_profile: Editar perfil
export: Exportar dados
- featured_tags: Hashtags destacadas
+ featured_tags: Hashtags em destaque
identity_proofs: Provas de identidade
import: Importar
import_and_export: Importar e exportar
@@ -1232,35 +1252,35 @@
preferences: Preferรชncias
profile: Perfil
relationships: Seguindo e seguidores
- two_factor_authentication: Autenticaรงรฃo em dois passos
+ two_factor_authentication: Autenticaรงรฃo de dois fatores
webauthn_authentication: Chaves de seguranรงa
statuses:
attached:
audio:
one: "%{count} รกudio"
other: "%{count} รกudios"
- description: 'Anexadas: %{attached}'
+ description: 'Anexado: %{attached}'
image:
one: "%{count} imagem"
other: "%{count} imagens"
video:
one: "%{count} vรญdeo"
other: "%{count} vรญdeos"
- boosted_from_html: Partilhadas de %{acct_link}
- content_warning: 'Aviso de conteรบdo: %{warning}'
+ boosted_from_html: Boost de %{acct_link}
+ content_warning: 'Aviso de Conteรบdo: %{warning}'
disallowed_hashtags:
- one: 'continha uma hashtag proibida: %{tags}'
- other: 'continha as hashtags proibidas: %{tags}'
+ one: 'continha hashtag nรฃo permitida: %{tags}'
+ other: 'continha hashtags nรฃo permitidas: %{tags}'
errors:
- in_reply_not_found: A publicaรงรฃo a que estรก a tentar responder parece nรฃo existir.
- language_detection: Detectar automaticamente a lรญngua
- open_in_web: Abrir no browser
- over_character_limit: limite de caracter excedeu %{max}
+ in_reply_not_found: Parece que a postagem ร  qual vocรช estรก tentando responder nรฃo existe.
+ language_detection: Detectar idioma automaticamente
+ open_in_web: Abrir no navegador
+ over_character_limit: limite de caracteres de %{max} excedido
pin_errors:
- limit: Jรก fixaste a quantidade mรกxima de publicaรงรตes
- ownership: Posts de outras pessoas nรฃo podem ser fixados
- private: Post nรฃo-pรบblico nรฃo pode ser fixado
- reblog: Nรฃo podes fixar uma partilha
+ limit: Quantidade mรกxima de toots excedida
+ ownership: Toots dos outros nรฃo podem ser fixados
+ private: Toots nรฃo-pรบblicos nรฃo podem ser fixados
+ reblog: Boosts nรฃo podem ser fixados
poll:
total_people:
one: "%{count} pessoa"
@@ -1273,109 +1293,107 @@
show_newer: Mostrar mais recentes
show_older: Mostrar mais antigos
show_thread: Mostrar conversa
- sign_in_to_participate: Inicie a sessรฃo para participar na conversa
- title: '%{name}: "%{quote}"'
+ sign_in_to_participate: Entre para participar dessa conversa
visibilities:
- direct: Direto
- private: Mostrar apenas para seguidores
- private_long: Mostrar apenas para seguidores
+ private: Privado
+ private_long: Posta apenas para seguidores
public: Pรบblico
- public_long: Todos podem ver
- unlisted: Pรบblico, mas nรฃo mostre no timeline pรบblico
- unlisted_long: Todos podem ver, porรฉm nรฃo serรก postado nas timelines pรบblicas
+ public_long: Posta em linhas pรบblicas
+ unlisted: Nรฃo-listado
+ unlisted_long: Nรฃo posta em linhas pรบblicas
stream_entries:
pinned: Toot fixado
- reblogged: partilhado
+ reblogged: deu boost
sensitive_content: Conteรบdo sensรญvel
tags:
- does_not_match_previous_name: nรฃo coincide com o nome anterior
+ does_not_match_previous_name: nรฃo corresponde ao nome anterior
terms:
body_html: |
- <h2>Polรญtica de privacidade</h2>
- <h3 id="collect">Que informaรงรฃo nรณs recolhemos?</h3>
+ <h2>Polรญtica de Privacidade</h2>
+ <h3 id="collect">Quais dados nรณs coletamos?</h3>
<ul>
- <li><em>Informaรงรฃo bรกsica da conta</em>: Se te registares neste servidor, pode-te ser pedido que indiques um nome de utilizador, um endereรงo de email e uma palavra-passe. Tambรฉm podes introduzir informaรงรฃo adicional de perfil, tal como um nome a mostrar e dados biogrรกficos, que carregues uma fotografia para o teu perfil e para o cabeรงalho. O nome de utilizador, o nome a mostrar, a biografia, a imagem de perfil e a imagem de cabeรงalho sรฃo sempre listados publicamente.</li>
- <li><em>Publicaรงรตes, seguimento e outra informaรงรฃo pรบblica</em>: A lista de pessoas que tu segues รฉ pรบblica, o mesmo รฉ verdade para os teus seguidores. Quando tu publicas uma mensagem, a data e a hora sรฃo guardados, tal como a aplicaรงรฃo a partir da qual a mensagem foi enviada. As mensagens podem conter anexos mรฉdia, tais como fotografias ou vรญdeos. Publicaรงรตes pรบblicas e nรฃo listadas sรฃo acessรญveis publicamente. Quando expรตes uma publicaรงรฃo no teu perfil, isso รฉ tambรฉm informaรงรฃo disponรญvel publicamente. As tuas publicaรงรตes sรฃo enviadas aos teus seguidores. Em alguns casos isso significa que elas sรฃo enviadas para servidores diferentes onde sรฃo guardadas cรณpias. Quando tu apagas publicaรงรตes, isso tambรฉm รฉ enviado para os teus seguidores. A aรงรฃo de republicar ou favoritar outra publicaรงรฃo รฉ sempre pรบblica.</li>
- <li><em>Publicaรงรตes diretas e exclusivas para seguidores</em>: Todas as publicaรงรตes sรฃo guardadas e processadas no servidor. Publicaรงรตes exclusivas para seguidores sรฃo enviadas para os teus seguidores e para utilizadores que sรฃo nelas mencionados. As publicaรงรตes diretas sรฃo enviadas apenas para os utilizadores nelas mencionados. Em alguns casos isso significa que elas sรฃo enviadas para diferentes servidores onde sรฃo guardadas cรณpias das mesmas. Nรณs fazemos um grande esforรงo para limitar o acesso a estas publicaรงรตes aos utilizadores autorizados, mas outros servidores podem falhar neste objetivo. Por isso, tu deves rever os servidores a que os teus seguidores pertencem. Tu podes ativar uma opรงรฃo para aprovar e rejeitar manualmente novos seguidores nas configuraรงรตes. <em>Por favor, tem em mente que os gestores do servidor e qualquer servidor que receba a publicaรงรฃo pode lรช-la</em> e que os destinatรกrios podem fazer uma captura de tela, copiar ou partilhar a publicaรงรฃo. <em>Nรฃo partilhes qualquer informaรงรฃo perigosa no Mastodon.</em></li>
- <li><em>IPs e outros metadados</em>: Quando inicias sessรฃo, nรณs guardamos o endereรงo de IP a partir do qual iniciaste a sessรฃo, tal como o nome do teu navegador. Todas as sessรตes estรฃo disponรญveis para verificaรงรฃo e revogaรงรฃo nas configuraรงรตes. O รบltimo endereรงo de IP usado รฉ guardado atรฉ 12 meses. Nรณs tambรฉm podemos guardar registos de servidor, os quais incluem o endereรงo de IP de cada pedido dirigido ao nosso servidor.</li>
+ <li><em>Dados bรกsicos de conta</em>: Se vocรช criar conta nesta instรขncia, um nome de usuรกrio, um e-mail e uma senha serรฃo exigidos. Vocรช tambรฉm pode adicionar dados extras como nome de exibiรงรฃo, biografia, imagem de perfil e capa. Com exceรงรฃo do e-mail e da senha, os dados citados sempre sรฃo pรบblicos.</li>
+ <li><em>Toots, seguindo e outros dados pรบblicos</em>: A lista de pessoas que vocรช segue e a sua lista de seguidores sรฃo pรบblicas. Ao enviar um toot, a data, a hora e o aplicativo usado sรฃo armazenados. Toots podem conter mรญdias anexadas, como รกudios, imagens e vรญdeos. Toots pรบblicos e nรฃo-listados sรฃo visรญveis publicamente. Os toots fixados no seu perfil sรฃo pรบblicos. Seus toots sรฃo enviados aos seus seguidores, em alguns casos isso significa que os toots sรฃo enviados para instรขncias diferentes e cรณpias sรฃo armazenadas lรก. Quando vocรช exclui toots, essa informaรงรฃo tambรฉm รฉ enviada aos seus seguidores. O ato de dar boost ou favoritar outro toot รฉ sempre pรบblico.</li></li>
+ <li><em>Mensagens Diretas e toots privados</em>: Todos os toots sรฃo armazenados e processados na instรขncia. Toots privados sรฃo enviados aos seus seguidores e aos usuรกrios mencionados neles; Mensagens Diretas (ou toots diretos) sรฃo enviadas somente aos usuรกrios mencionados nelas. Em alguns casos isso significa que os toots sรฃo enviados para instรขncias diferentes e cรณpias sรฃo armazenadas lรก. Nรณs trabalhamos constantemente para limitar o acesso a estes toots somente ร s pessoas autorizadas, porรฉm outras instรขncias podem nรฃo fazer o mesmo. Portanto, รฉ importante analisar as instรขncias dos seus seguidores. Vocรช pode trancar a conta para aprovar ou vetar novos seguidores manualmente nas configuraรงรตes. <em>Por favor, tenha em mente que os operadores da instรขncia em que se estรก e das instรขncias receptoras podem ver tais toots</em>, e que os destinatรกrios podem fazer capturas de tela, copiar ou usar outra maneira para compartilhar os toots. <em>Nรฃo compartilhe informaรงรฃo confidencial pelo Mastodon.</em></li>
+ <li><em>IPs e outros metadados</em>: Ao entrar na sua conta, nรณs armazenamos o seu endereรงo de IP e o nome do navegador usado. Todas as sessรตes abertas estรฃo disponรญveis para serem analisadas e revogadas nas configuraรงรตes. O รบltimo endereรงo de IP usado รฉ armazenado por atรฉ 12 meses. Nรณs tambรฉm podemos reter histรณricos da instรขncia que incluem o endereรงo de IP de todas as conexรตes ร  nossa instรขncia.</li>
</ul>
<hr class="spacer" />
- <h3 id="use">Para que usamos a tua informaรงรฃo?</h3>
+ <h3 id="use">Como usamos os seus dados?</h3>
- <p>Qualquer informaรงรฃo que recolhemos sobre ti pode ser usada dos seguintes modos:</p>
+ <p>Todo dado que coletamos pode ser usado das seguintes maneiras:</p>
<ul>
- <li>Para providenciar a funcionalidade central do Mastodon. Tu sรณ podes interagir com o conteรบdo de outras pessoas e publicar o teu prรณprio conteรบdo depois de teres iniciado sessรฃo. Por exemplo, tu podes seguir outras pessoas para veres as suas publicaรงรตes na tua cronologia inicial personalizada. </li>
- <li>Para ajudar na moderaรงรฃo da comunidade para, por exemplo, comparar o teu endereรงo IP com outros conhecidos, para determinar a fuga ao banimento ou outras violaรงรตes.</li>
- <li>O endereรงo de e-mail que tu forneces pode ser usado para te enviar informaรงรตes e/ou notificaรงรตes sobre outras pessoas que estรฃo a interagir com o teu conteรบdo ou a enviar-te mensagens, para responderes a inquรฉritos e/ou outros pedidos ou questรตes.</li>
+ <li>Para prover a funcionalidade bรกsica do Mastodon. Vocรช sรณ pode interagir com o conteรบdo de outras pessoas e postar seu prรณprio conteรบdo usando uma conta. Por exemplo, vocรช pode seguir outras pessoas para ver seus toots na sua prรณpria linha do tempo personalizada.</li>
+ <li>Para auxiliar na moderaรงรฃo da comunidade, por exemplo ao comparar o seu endereรงo de IP com outros endereรงos de IP conhecidos para determinar evasรฃo de banimento e outras violaรงรตes.</li>
+ <li>O endereรงo de e-mail que vocรช fornecer pode ser usado para te enviar informaรงรตes, notificaรงรตes sobre outras pessoas interagindo com o seu conteรบdo ou contigo e para responder a questรตes ou outras solicitaรงรตes.</li>
</ul>
<hr class="spacer" />
- <h3 id="protect">Como รฉ que nรณs protegemos a tua informaรงรฃo?</h3>
+ <h3 id="protect">Como protegemos seus dados?</h3>
- <p>Nรณs implementamos uma variedade de medidas de seguranรงa para garantir a seguranรงa da tua informaรงรฃo pessoal quando tu introduzes, submetes ou acedes ร  mesma. Entre outras coisas, a tua sessรฃo de navegaรงรฃo, tal como o trรกfego entre as tuas aplicaรงรตes e a API, estรฃo seguras por SSL e a tua palavra-passe รฉ codificada usando um forte algoritmo de sentido รบnico. Tu podes activar a autenticaรงรฃo em dois passos para aumentares ainda mais a seguranรงa do acesso ร  tua conta.</p>
+ <p>Nรณs implementamos diversas medidas de seguranรงa para manter suas informaรงรตes pessoais seguras quando vocรช as acessa ou as envia. Entre outras coisas, sua sessรฃo do navegador, bem como o trรกfego entre os aplicativos e a API sรฃo asseguradas usando SSL e a sua senha รฉ guardada usando um algoritmo forte de criptografia de mรฃo รบnica. Vocรช pode ativar autenticaรงรฃo em dois fatores como forma de aumentar a seguranรงa no acesso ร  sua conta.</p>
<hr class="spacer" />
<h3 id="data-retention">Qual รฉ a nossa polรญtica de retenรงรฃo de dados?</h3>
- <p>Nรณs envidaremos todos os esforรงos no sentido de:</p>
+ <p>Nรณs trabalhamos constantemente para:</p>
<ul>
- <li>Guardar registos do servidor contendo o endereรงo de IP de todos os pedidos feitos a este servidor, considerando que estes registos nรฃo serรฃo guardados por mais de 90 dias.</li>
- <li>Guardar os endereรงos de IP associados aos utilizadores registados durante um perรญodo que nรฃo ultrapassarรก os 12 meses.</li>
+ <li>Reter o histรณrico da instรขncia contendo os endereรงos de IP de todas as conexรตes a essa instรขncia. O histรณrico รฉ mantido por nรฃo mais que 90 dias.</li>
+ <li>Reter os endereรงos de IP associados ร  usuรกrios da instรขncia por nรฃo mais que 12 meses.</li>
</ul>
- <p>Tu podes pedir e descarregar um ficheiro com o teu conteรบdo, incluindo as tuas publicaรงรตes, os ficheiros multimรฉdia, a imagem de perfil e a imagem de cabeรงalho.</p>
+ <p>Vocรช pode solicitar e baixar um arquivo de todo o conteรบdo da sua conta, incluindo seus toots, suas mรญdias, imagem de perfil e capa.</p>
- <p>Tu podes apagar a tua conta de modo definitivo e a qualquer momento.</p>
+ <p>YVocรช pode excluir a sua conta irreversivelmente a qualquer momento.</p>
<hr class="spacer"/>
- <h3 id="cookies">Usamos cookies?</h3>
+ <h3 id="cookies">Nรณs usamos cookies?</h3>
- <p>Sim. Cookies sรฃo pequenos ficheiros que um site ou o seu fornecedor de serviรงo transfere para o disco rรญgido do teu computador atravรฉs do teu navegador (se tu permitires). Estes cookies permitem ao site reconhecer o teu navegador e, se tu tiveres uma conta registada, associรก-lo a ela.</p>
+ <p>Sim. Cookies sรฃo pequenos arquivos que um site ou serviรงo baixa atravรฉs do seu navegador (se vocรช permitir). Esses cookies permitem ao site conhecer seu navegador e, se vocรช tiver uma conta, associรก-lo a ela.</p>
- <p>Nรณs usamos os cookies para compreender e guardar as tuas preferรชncias para as visitas futuras.</p>
+ <p>Nรณs usamos cookies para salvar suas preferรชncias para futuras visitas.</p>
<hr class="spacer" />
- <h3 id="disclose">Nรณs divulgamos alguma informaรงรฃo para entidades externas?</h3>
+ <h3 id="disclose">Nรณs compartilhamos algum dado para terceiros?</h3>
- <p>Nรณs nรฃo vendemos, trocamos ou transferimos de qualquer modo a tua informaรงรฃo pessoal que seja identificรกvel para qualquer entidade externa. Isto nรฃo inclui terceiros de confianรงa que nos ajudam a manter o nosso site, conduzir o nosso negรณcio ou prestar-te este serviรงo, desde que esses terceiros concordem em manter essa informaรงรฃo confidencial. Poderemos tambรฉm revelar a tua informaรงรฃo quando nรณs acreditamos que isso รฉ apropriado para cumprir a lei, forรงar a aplicaรงรฃo dos nossos termos de serviรงo ou proteger os direitos, propriedade e seguranรงa, nossos e de outrem.</p>
+ <p>Nรณs nรฃo vendemos, trocamos ou compartilhamos de qualquer maneira dados que possam te identificar ร  terceiros. Isso nรฃo inclui terceiros confiรกveis que nos auxiliam a operar o nosso site, realizar nosso serviรงo ou prestar assistรชncia, contanto que esses terceiros se comprometam a manter essa informaรงรฃo confidencial. Nรณs podemos tambรฉm divulgar informaรงรฃo quando acreditamos que รฉ apropriado para obedecer a lei, para fazer cumprir nossas polรญticas ou proteger os nossos direitos, propriedade ou seguranรงa, ou de outrรฉm.</p>
- <p>O teu conteรบdo pรบblico pode ser descarregado por outros servidores na rede. As tuas publicaรงรตes pรบblicas e exclusivas para os teus seguidores sรฃo enviadas para os servidores onde os teus seguidores residem e as mensagens directas sรฃo entregues aos servidores dos seus destinatรกrios, no caso desses seguidores ou destinatรกrios residirem num servidor diferente deste.</p>
+ <p>Seu conteรบdo pรบblico pode ser acessado por outras instรขncias na rede. Seus toots pรบblicos e privados sรฃo enviados ร s instรขncias dos seus seguidores e seus toots diretos sรฃo enviados ร s instรขncias dos usuรกrios mencionados neles, contanto que esses seguidores ou usuรกrios estejam em uma instรขncia diferente desta.</p>
- <p>Quando tu autorizas uma aplicaรงรฃo a usar a tua conta, dependendo da abrangรชncia das permissรตes que tu aprovas, ela pode ter acesso ร  informaรงรฃo pรบblica do teu perfil, ร  lista de quem segues, aos teus seguidores, ร s tuas listas, a todas as tuas publicaรงรตes e aos teus favoritos. As aplicaรงรตes nunca terรฃo acesso ao teu endereรงo de e-mail ou ร  tua palavra-passe.</p>
+ <p>Quando vocรช autoriza um aplicativo a usar sua conta, dependendo do nรญvel de autorizaรงรฃo das permissรตes que vocรช aprovar, o aplicativo pode acessar seus dados pรบblicos, a lista de usuรกrios que vocรช segue, seus seguidores, suas listas, suas Mensagens Diretas e seus toots favoritos. Aplicativos nunca podem acessar o seu endereรงo de e-mail ou senha.</p>
<hr class="spacer" />
- <h3 id="children">Utilizaรงรฃo do site por crianรงas</h3>
+ <h3 id="children">Uso desse site por crianรงas</h3>
- <p>Se este servidor estiver na EU ou na EEA: O nosso site, produtos e serviรงos sรฃo todos dirigidos a pessoas que tรชm, pelo menos, 16 de idade. Se tu tens menos de 16 anos, devido aos requisitos da GDPR (<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">General Data Protection Regulation</a>) nรฃo uses este site.</p>
+ <p>Se a instรขncia estรก na UE ou no EEE: Nosso site, produto e serviรงo sรฃo direcionados ร s pessoas que tem ao menos 16 anos de idade. Se vocรช tem menos de 16 anos, de acordo com os requisitos da RGPD (<a href="https://pt.wikipedia.org/wiki/Regulamento_Geral_sobre_a_Prote%C3%A7%C3%A3o_de_Dados">Regulamento Geral sobre Proteรงรฃo de Dados</a>) nรฃo use este site.</p>
- <p>Se este servidor estiver nos EUA: O nosso site, produtos e serviรงos sรฃo todos dirigidos a pessoas que tรชm, pelo menos, 13 anos de idade. Se tu tens menos de 13 anos de idade, devido aos requisitos da COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) nรฃo uses este site.</p>
+ <p>Se esta instรขncia estรก nos EUA: Nosso site, produto e serviรงo sรฃo direcionados ร s pessoas que tem ao menos 13 anos de idade. Se vocรช tem menos de 13 anos, de acordo com os requerimentos da COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) nรฃo use este site</p>
- <p>Os requisitos legais poderรฃo ser diferentes se este servidor estiver noutra jurisdiรงรฃo.</p>
+ <p>Os requisitos da lei podem ser diferentes em outra jurisdiรงรฃo.</p>
<hr class="spacer" />
- <h3 id="changes">Alteraรงรตes ร  nossa Polรญtica de Privacidade</h3>
+ <h3 id="changes">Alteraรงรตes na nossa Polรญtica de Privacidade</h3>
- <p>Se nรณs decidirmos alterar a nossa polรญtica de privacidade, nรณs iremos publicar essas alteraรงรตes nesta pรกgina.</p>
+ <p>Se decidirmos mudar nossa Polรญtica de Privacidade, iremos disponibilizar as alteraรงรตes nesta pรกgina.</p>
- <p>Este documento รฉ CC-BY-SA. Ele foi actualizado pela รบltima vez em 7 de Marรงo 2018.</p>
+ <p>CC-BY-SA. Atualizado pela รบltima vez em 7 de marรงo de 2018.</p>
- <p>Originalmente adaptado de <a href="https://github.com/discourse/discourse">Discourse privacy policy</a>.</p>
- title: "%{instance} Termos de Serviรงo e Polรญtica de Privacidade"
+ <p>Adaptado originalmente de <a href="https://github.com/discourse/discourse">Polรญtica de Privacidade Discourse</a>.</p>
+ title: Termos de Serviรงo e Polรญtica de Privacidade de %{instance}
themes:
- contrast: Mastodon (Elevado contraste)
- default: Mastodon
- mastodon-light: Mastodon (Claro)
+ contrast: Mastodon (Alto contraste)
+ default: Mastodon (Noturno)
+ mastodon-light: Mastodon (Diurno)
time:
formats:
default: "%H:%M em %d de %b de %Y"
@@ -1383,99 +1401,109 @@
two_factor_authentication:
add: Adicionar
disable: Desativar
- disabled_success: Autenticaรงรฃo em duas etapas desativada com sucesso
+ disabled_success: Autenticaรงรฃo de dois fatores desativada com sucesso
edit: Editar
- enabled: A autenticaรงรฃo em dois passos estรก ativada
- enabled_success: Autenticaรงรฃo em dois passos ativada com sucesso
- generate_recovery_codes: Gerar cรณdigos para recuperar conta
- lost_recovery_codes: Cรณdigos de recuperaรงรฃo permite que vocรช recupere o acesso a sua conta se vocรช perder seu telefone. Se vocรช perder os cรณdigos de recuperaรงรฃo, vocรช pode regera-los aqui. Seus cรณdigos antigos serรฃo invalidados.
- methods: Mรฉtodos de duas etapas
- otp: Aplicaรงรฃo de autenticaรงรฃo
- recovery_codes: Cรณpia de seguranรงa dos cรณdigos de recuperaรงรฃo
- recovery_codes_regenerated: Cรณdigos de recuperaรงรฃo foram gerados com sucesso
- recovery_instructions_html: Se tu alguma vez perderes o teu smartphone, to poderรกs usar um dos cรณdigos de recuperaรงรฃo para voltares a ter acesso ร  tua conta. <strong>Mantรฉm os cรณdigos de recuperaรงรฃo seguros</strong>. Por exemplo, tu podes imprimi-los e guardรก-los junto a outros documentos importantes.
+ enabled: Autenticaรงรฃo de dois fatores ativada
+ enabled_success: Autenticaรงรฃo de dois fatores ativada com sucesso
+ generate_recovery_codes: Gerar cรณdigos de recuperaรงรฃo
+ lost_recovery_codes: Cรณdigos de recuperaรงรฃo permitem que vocรช recupere o acesso ร  sua conta caso perca o seu celular. Se vocรช perdeu seus cรณdigos de recuperaรงรฃo, vocรช pode gerรก-los novamente aqui. Seus cรณdigos de recuperaรงรฃo anteriores serรฃo invalidados.
+ methods: Mรฉtodos de dois fatores
+ otp: Aplicativo autenticador
+ recovery_codes: Cรณdigos de recuperaรงรฃo de reserva
+ recovery_codes_regenerated: Cรณdigos de recuperaรงรฃo regerados com sucesso
+ recovery_instructions_html: Se vocรช perder acesso ao seu celular, vocรช pode usar um dos cรณdigos de recuperaรงรฃo abaixo para acessar a sua conta. <strong>Mantenha os cรณdigos de recuperaรงรฃo em um local seguro</strong>. Por exemplo, vocรช pode imprimi-los e guardรก-los junto com outros documentos importantes.
webauthn: Chaves de seguranรงa
user_mailer:
backup_ready:
- explanation: Pediste uma cรณpia completa da tua conta Mastodon. Ela jรก estรก pronta para descarregares!
- subject: O teu arquivo estรก pronto para descarregar
- title: Arquivo de ficheiros
+ explanation: Vocรช pediu um backup completo da sua conta no Mastodon. E agora estรก pronto para ser baixado!
+ subject: Seu arquivo estรก pronto para download
+ title: Baixar arquivo
sign_in_token:
details: 'Aqui estรฃo os detalhes da tentativa:'
- explanation: 'Detectamos uma tentativa de entrar na sua conta a partir de um endereรงo IP nรฃo reconhecido. Se รฉ vocรช, por favor, insira o cรณdigo de seguranรงa abaixo na pรกgina de acesso:'
- further_actions: 'Se nรฃo foi vocรช, por favor altere sua senha e ative a autenticaรงรฃo de dois fatores na sua conta. Pode fazรช-lo aqui:'
- subject: Por favor, confirme a tentativa de acesso
+ explanation: 'Detectamos uma tentativa de acessar sua conta a partir de um endereรงo IP nรฃo reconhecido. Se for vocรช, insira o cรณdigo de seguranรงa abaixo na pรกgina de desafio:'
+ further_actions: 'Se nรฃo foi vocรช, por favor mude sua senha e ative a autenticaรงรฃo de dois fatores em sua conta. Vocรช pode fazรช-lo aqui:'
+ subject: Confirme a tentativa de login
title: Tentativa de acesso
+ status_removed:
+ explanation: We use artificial intelligence (AI) to assist our hardworking moderators, and some Truths are flagged for deletion or marked โ€œsensitiveโ€ by AI. While the AI we use is very good, it is not error-proof. Assisted by technology, our moderators use their best judgment to ensure compliance with our Terms of Service. Please give our team time to review your Truth to determine whether it violates our Terms of Service. After a thorough review, we will reinstate the Truth or uphold its removal.
+ title: Your Truth was flagged for review
+ subject: Sua Truth foi sinalizada para revisรฃo
warning:
explanation:
- disable: Enquanto a tua conta estรก congelada, os seus dados permanecem intactos, mas tu nรฃo podes executar quaisquer acรงรตes atรฉ que ela seja desbloqueada.
- sensitive: Os seus ficheiros de media carregados e os media ligados serรฃo tratados como sensรญveis.
- silence: Enquanto a sua conta estiver limitada, sรณ pessoas que jรก estiver a seguir irรฃo ver as suas publicaรงรตes nesta instรขncia e poderรก ser excluรญdo de vรกrias listagens pรบblicas. No entanto, outros ainda o poderรฃo seguir de forma manual.
- suspend: A sua conta foi suspensa e todas as suas publicaรงรตes e os seus ficheiros de media foram irreversivelmente removidos desta instรขncia e das instรขncias onde tinhas seguidores.
- verify: Vocรช รฉ um membro certificado da comunidade
- unverify: Vocรช nรฃo รฉ mais um membro certificado da comunidade.
- get_in_touch: Pode responder a este e-mail para entrar em contacto com a equipa de %{instance}.
- review_server_policies: Reveja a polรญtica da instรขncia
+ ban_html: Sua conta foi banida por violar nossos Termos de Serviรงo. Banimentos sem prazo definido sรฃo um tipo de sanรงรฃo raro e muito grave, geralmente aplicados somente ร s mais sรฉrias violaรงรตes de nossos Termos de Serviรงo. Se vocรช acredita que nossa decisรฃo de banir sua conta foi injusta, imoral ou simplesmente errada, recomendamos que entre com um recurso. Envie um e-mail para %{email}. Na linha de assunto, escreva "@Appeal" junto com seu nome de usuรกrio. Aguarde 48 horas para que nossa equipe avalie seu recurso contra o banimento. Apรณs uma avaliaรงรฃo cuidadosa, decidiremos reverter ou manter o banimento.
+ disable_html: Vocรช nรฃo pode mais entrar na sua conta nem usรก-la de qualquer forma que seja, mas seu perfil e seus outros dados permanecerรฃo intactos.
+ sensitive_html: Os arquivos de mรญdia que vocรช carregou e os links de mรญdia que publicou serรฃo tratados como confidenciais.
+ silence_html: Vocรช continua podendo usar sua conta, mas apenas as pessoas que jรก seguem vocรช verรฃo suas postagens nesse servidor. Alรฉm disso, vocรช poderรก ser excluรญdo de vรกrias listagens pรบblicas. No entanto, as pessoas continuam podendo seguir vocรช manualmente.
+ suspend_html: Sua conta foi suspensa e ficarรก inacessรญvel por %{suspension_duration}.
+ suspend_indefinite_html: Sua conta foi suspensa e ficarรก inacessรญvel.
+ verify_html: Vocรช รฉ um membro certificado da comunidade
+ unverify_html: Vocรช nรฃo รฉ mais um membro certificado da comunidade
+ get_in_touch: Vocรช pode responder a este e-mail para entrar em contato com a equipe de %{instance}.
+ review_server_policies: Revisar as polรญticas da instรขncia
statuses: 'Especificamente, para:'
subject:
- disable: A tua conta %{acct} foi congelada
+ disable: Sua conta %{acct} foi bloqueada
none: Aviso para %{acct}
- sensitive: As publicaรงรตes de media da sua conta %{acct} foram marcadas como sensรญveis
- silence: A tua conta %{acct} foi limitada
- suspend: A tua conta %{acct} foi suspensa
- verify: Sua conta %{acct} foi verificada
+ sensitive: Sua conta %{acct} de postagem de mรญdia foi marcada como sensรญvel
+ silence: Sua conta %{acct} foi silenciada
+ suspend: Sua conta %{acct} foi banida
+ verify: Sua conta% {acct} foi verificada
unverify: A verificaรงรฃo% {acct} da sua conta foi removida
title:
- disable: Conta congelada
+ disable: Conta bloqueada
none: Aviso
- sensitive: A sua media foi marcada como sensรญvel
- silence: Conta limitada
- suspend: Conta suspensa
+ sensitive: Sua mรญdia foi marcada como sensรญvel
+ silence: Conta silenciada
+ suspend: Conta banida
verify: Conta verificada
unverify: Verificaรงรฃo de conta removida
+ waitlisted:
+ title: Sua conta foi criada com sucesso!
welcome:
- edit_profile_action: Configura o perfil
- edit_profile_step: Podes personalizar o teu perfil carregando uma imagem de perfil e de cabeรงalho ou alterando o nome a exibir, entre outras opรงรตes. Se preferires rever os novos seguidores antes deles te poderem seguir, podes tornar a tua conta privada.
- explanation: Aqui estรฃo algumas dicas para comeรงares
- final_action: Comeรงa a publicar
- final_step: 'Comeรงa a publicar! Mesmo sem seguidores, as tuas mensagens pรบblicas podem ser vistas por outros, por exemplo, na cronologia local e em hashtags. Tu podes querer apresentar-te na hashtag #introductions.'
- full_handle: O teu nome completo
- full_handle_hint: Isto รฉ o que vocรช diria aos seus amigos para que eles lhe possam enviar mensagens ou seguir a partir de outra instรขncia.
+ edit_profile_action: Configurar perfil
+ edit_profile_step: Vocรช pode personalizar o seu perfil enviando um avatar, uma capa, alterando seu nome de exibiรงรฃo e etc. Se vocรช preferir aprovar seus novos seguidores antes de eles te seguirem, vocรช pode trancar a sua conta.
+ explanation: Aqui estรฃo algumas dicas para vocรช comeรงar
+ final_action: Comece a tootar
+ final_step: 'Comece a tootar! Mesmo sem seguidores, suas mensagens pรบblicas podem ser vistas pelos outros, por exemplo, na linha local e nas hashtags. Vocรช pode querer fazer uma introduรงรฃo usando a hashtag #introduรงรฃo, ou em inglรชs usando a hashtag #introductions.'
+ full_handle: Seu nome de usuรกrio completo
+ full_handle_hint: Isso รฉ o que vocรช compartilha com aos seus amigos para que eles possam te mandar toots ou te seguir a partir de outra instรขncia.
review_preferences_action: Alterar preferรชncias
- review_preferences_step: Certifica-te de configurar as tuas preferรชncias, tais como os e-mails que gostarias de receber ou o nรญvel de privacidade que desejas que as tuas publicaรงรตes tenham por defeito. Se nรฃo sofres de enjoo, podes ativar a opรงรฃo de auto-iniciar GIFs.
- subject: Bem-vindo ao Mastodon
- tip_federated_timeline: A cronologia federativa รฉ uma visรฃo global da rede Mastodon. Mas sรณ inclui pessoas que os teus vizinhos subscrevem, por isso nรฃo รฉ uma visรฃo completa.
- tip_following: Segues o(s) administrador(es) do teu servidor por defeito. Para encontrar mais pessoas interessantes, procura nas cronologias local e federada.
- tip_local_timeline: A cronologia local รฉ uma visรฃo global das pessoas em %{instance}. Estes sรฃo os seus vizinhos mais prรณximos!
- tip_mobile_webapp: Se o teu navegador mรณvel te oferecer a possibilidade de adicionar o Mastodon ao teu homescreen, tu podes receber notificaรงรตes push. Ele age como uma aplicaรงรฃo nativa de vรกrios modos!
+ review_preferences_step: Nรฃo se esqueรงa de configurar suas preferรชncias, como quais e-mails vocรช gostaria de receber, que nรญvel de privacidade vocรช gostaria que seus toots tenham por padrรฃo. Se vocรช nรฃo sofre de enjoo com movimento, vocรช pode habilitar GIFs animado automaticamente.
+ subject: Boas-vindas ao Mastodon
+ tip_federated_timeline: A linha global รฉ uma visรฃo contรญnua da rede do Mastodon. Mas ela sรณ inclui pessoas de instรขncias que a sua instรขncia conhece, entรฃo nรฃo รฉ a rede completa.
+ tip_following: Vocรช vai seguir administrador(es) da sua instรขncia por padrรฃo. Para encontrar mais gente interessante, confira as linhas local e global.
+ tip_local_timeline: A linha local รฉ uma visรฃo contรญnua das pessoas em %{instance}. Estes sรฃo seus vizinhos!
+ tip_mobile_webapp: Se o seu navegador mรณvel oferecer a opรงรฃo de adicionar Mastodon ร  tela inicial, vocรช pode receber notificaรงรตes push. Serรก como um aplicativo nativo!
tips: Dicas
- title: Bem-vindo a bordo, %{name}!
+ title: Boas vindas, %{name}!
users:
- follow_limit_reached: Nรฃo podes seguir mais do que %{limit} pessoas
- generic_access_help_html: Problemas para aceder ร  sua conta? Pode entrar em contacto com %{email} para obter ajuda
- invalid_otp_token: Cรณdigo de autenticaรงรฃo invรกlido
- invalid_sign_in_token: Cรณgido de seguranรงa invรกlido
- otp_lost_help_html: Se tu perdeste acesso a ambos, tu podes entrar em contacto com %{email}
- seamless_external_login: Tu estรกs ligado via um serviรงo externo. Por isso, as configuraรงรตes da palavra-passe e do e-mail nรฃo estรฃo disponรญveis.
- signed_in_as: 'Registado como:'
- suspicious_sign_in_confirmation: Parece que nรฃo iniciou sessรฃo atravรฉs deste dispositivo antes, e nรฃo acede ร  sua conta hรก algum tempo. Portanto, enviรกmos um cรณdigo de seguranรงa para o seu endereรงo de e-mail para confirmar que รฉ vocรช.
+ follow_limit_reached: Vocรช nรฃo pode seguir mais de %{limit} pessoas
+ generic_access_help_html: Problemas para acessar sua conta? Vocรช pode entrar em contato com %{email} para obter ajuda
+ invalid_otp_token: Cรณdigo de dois fatores invรกlido
+ invalid_sign_in_token: Cรณdigo de seguranรงa invรกlido
+ otp_lost_help_html: Se vocรช perder o acesso ร  ambos, vocรช pode entrar em contato com %{email}
+ previously_used_password: Por favor, use uma senha que vocรช nรฃo usou anteriormente.
+ password_mismatch: A senha e a confirmaรงรฃo da senha nรฃo coincidem.
+ seamless_external_login: Vocรช entrou usando um serviรงo externo, entรฃo configuraรงรตes de e-mail e senha nรฃo estรฃo disponรญveis.
+ signed_in_as: 'Entrou como:'
+ suspicious_sign_in_confirmation: Parece que vocรช nรฃo fez login deste dispositivo antes, e vocรช nรฃo fez login por um tempo. Portanto, estamos enviando um cรณdigo de seguranรงa para o seu endereรงo de e-mail para confirmar que รฉ vocรช.
verification:
- explanation_html: 'Pode <strong>comprovar que รฉ o dono dos links nos metadados do seu perfil</strong>. Para isso, o website para o qual o link aponta tem de conter um link para o seu perfil do Mastodon. Este link <strong>tem</strong> de ter um atributo <code>rel="me"</code>. O conteรบdo do texto nรฃo รฉ relevante. Aqui estรก um exemplo:'
+ explanation_html: 'Vocรช pode <strong>verificar os links nos metadados do seu perfil</strong>. Para isso, o site citado deve conter um link de volta para o seu perfil do Mastodon. O link de volta <strong>deve</strong> conter um atributo <code>rel="me"</code>. O conteรบdo ou texto do link nรฃo importa. Aqui estรก um exemplo:'
verification: Verificaรงรฃo
webauthn_credentials:
add: Adicionar nova chave de seguranรงa
create:
- error: Ocorreu um problema ao adicionar sua chave de seguranรงa. Tente novamente.
+ error: Houve um problema ao adicionar sua chave de seguranรงa. Tente novamente.
success: A sua chave de seguranรงa foi adicionada com sucesso.
- delete: Eliminar
- delete_confirmation: Tem a certeza de que pretende eliminar esta chave de seguranรงa?
- description_html: Se vocรช ativar a <strong>autenticaรงรฃo com chave de seguranรงa</strong>, para aceder ร  sua conta serรก necessรกrio que utilize uma das suas chaves de seguranรงa.
+ delete: Excluir
+ delete_confirmation: Vocรช tem certeza de que deseja excluir esta chave de seguranรงa?
+ description_html: Se vocรช habilitar a <strong>autenticaรงรฃo por chave de seguranรงa</strong>, o login exigirรก que vocรช use uma das suas chaves de seguranรงa.
destroy:
- error: Ocorreu um problema ao remover a sua chave de seguranรงa. Tente novamente.
- success: A sua chave de seguranรงa foi eliminada com sucesso.
+ error: Houve um problema ao excluir sua chave de seguranรงa. Tente novamente.
+ success: Sua chave de seguranรงa foi excluรญda com sucesso.
invalid_credential: Chave de seguranรงa invรกlida
- nickname_hint: Introduza o apelido da sua nova chave de seguranรงa
- not_enabled: Ainda nรฃo ativou o WebAuthn
- not_supported: Este navegador nรฃo suporta chaves de seguranรงa
- otp_required: Para usar chaves de seguranรงa, por favor ative primeiro a autenticaรงรฃo de duas etapas.
- registered_on: Registado em %{date}
+ nickname_hint: Digite o apelido da sua nova chave de seguranรงa
+ not_enabled: Vocรช ainda nรฃo habilitou o WebAuthn
+ not_supported: Este navegador nรฃo tem suporte a chaves de seguranรงa
+ otp_required: Para usar chaves de seguranรงa, por favor habilite primeiro a autenticaรงรฃo de dois fatores.
+ registered_on: Registrado em %{date}
diff -ru truth-old/opensource/config/navigation.rb truth-new/opensource/config/navigation.rb
--- truth-old/opensource/config/navigation.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/navigation.rb 2024-04-01 14:59:13
@@ -26,7 +26,6 @@
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s|
s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports}
- s.item :reports, safe_join([fa_icon('bullhorn fw'), t('admin.trending_truths.title')]), admin_trending_truths_url, highlights_on: %r{/admin/trending_truths}
s.item :exports, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url, highlights_on: %r{/settings/export}
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts}
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path
@@ -45,7 +44,6 @@
s.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis}
s.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? && !whitelist_mode? }, highlights_on: %r{/admin/relays}
s.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? }
- s.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? }
end
n.item :logout, safe_join([fa_icon('sign-out fw'), t('auth.logout')]), destroy_user_session_url, link_html: { 'data-method' => 'delete' }
Only in truth-old/opensource/config: pghero.yml
diff -ru truth-old/opensource/config/routes.rb truth-new/opensource/config/routes.rb
--- truth-old/opensource/config/routes.rb 2022-06-08 09:15:38
+++ truth-new/opensource/config/routes.rb 2024-04-12 09:09:08
@@ -5,6 +5,7 @@
Rails.application.routes.draw do
root 'home#index'
+ resources :apidocs, only: [:index]
mount LetterOpenerWeb::Engine, at: 'letter_opener' if Rails.env.development?
@@ -12,7 +13,6 @@
authenticate :user, lambda { |u| u.admin? } do
mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
- mount PgHero::Engine, at: 'pghero', as: :pghero
end
namespace :oauth do
@@ -27,17 +27,17 @@
tokens: 'oauth/tokens'
end
+ get '.well-known/host-meta', to: 'well_known/host_meta#show', as: :host_meta, defaults: { format: 'xml' }
+ get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger
+ get '.well-known/change-password', to: redirect('/auth/edit')
+ post '.well-known/skadnetwork/report-attribution/', to: 'well_known/skadnetwork#create'
+
get 'manifest', to: 'manifests#show', defaults: { format: 'json' }
get 'intent', to: 'intents#show'
get 'custom.css', to: 'custom_css#show', as: :custom_css
-
- resource :instance_actor, path: 'actor', only: [:show] do
- resource :inbox, only: [:create], module: :activitypub
- resource :outbox, only: [:show], module: :activitypub
- end
-
get '/unsubscribe', to: 'unsubscribe#unsubscribe'
+ get '/link/:id', to: 'link#show', as: :link
devise_scope :user do
get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite
@@ -50,16 +50,16 @@
devise_for :users, path: 'auth', controllers: {
omniauth_callbacks: 'auth/omniauth_callbacks',
- sessions: 'auth/sessions',
- registrations: 'auth/registrations',
- passwords: 'auth/passwords',
- confirmations: 'auth/confirmations',
+ sessions: 'auth/sessions',
+ registrations: 'auth/registrations',
+ passwords: 'auth/passwords',
+ confirmations: 'auth/confirmations',
}
get '/users/:username', to: redirect('/@%{username}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
resources :accounts, path: 'users', only: [:show], param: :username do
- get :remote_follow, to: 'remote_follow#new'
+ get :remote_follow, to: 'remote_follow#new'
post :remote_follow, to: 'remote_follow#create'
resources :statuses, only: [:show] do
@@ -76,15 +76,11 @@
resource :follow, only: [:create], controller: :account_follow
resource :unfollow, only: [:create], controller: :account_unfollow
- resource :outbox, only: [:show], module: :activitypub
- resource :inbox, only: [:create], module: :activitypub
resource :claim, only: [:create], module: :activitypub
resources :collections, only: [:show], module: :activitypub
resource :followers_synchronization, only: [:show], module: :activitypub
end
- resource :inbox, only: [:create], module: :activitypub
-
get '/@:username', to: 'accounts#show', as: :short_account
get '/@:username/with_replies', to: 'accounts#show', as: :short_account_with_replies
get '/@:username/media', to: 'accounts#show', as: :short_account_media
@@ -92,7 +88,7 @@
get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status
get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status
- get '/interact/:id', to: 'remote_interaction#new', as: :remote_interaction
+ get '/interact/:id', to: 'remote_interaction#new', as: :remote_interaction
post '/interact/:id', to: 'remote_interaction#create'
get '/explore', to: 'directories#index', as: :explore
@@ -135,7 +131,6 @@
resources :webauthn_credentials, only: [:index, :new, :create, :destroy],
path: 'security_keys',
controller: 'two_factor_authentication/webauthn_credentials' do
-
collection do
get :options
end
@@ -170,7 +165,7 @@
get :player
end
- resources :tags, only: [:show]
+ resources :tags, only: [:show]
resources :emojis, only: [:show]
resources :invites, only: [:index, :create, :destroy]
resources :filters, except: [:show, :index]
@@ -235,8 +230,6 @@
resources :reported_statuses, only: [:create]
end
- resources :trending_truths, only: [:index, :update, :destroy]
-
resources :report_notes, only: [:create, :destroy]
resources :accounts, only: [:index, :show, :destroy] do
@@ -331,6 +324,8 @@
end
namespace :api do
+ get '/docs', to: 'docs#index'
+
# OEmbed
get '/oembed', to: 'oembed#show', as: :oembed
@@ -340,7 +335,7 @@
post :change_email, controller: 'user_settings'
post :delete_account, controller: 'user_settings'
- scope :accounts, defaults: { format: 'json' } do
+ scope :accounts, defaults: { format: 'json' } do
get :mfa, to: 'accounts#mfa'
scope :mfa do
scope :setup do
@@ -377,6 +372,10 @@
post :unpin, to: 'pins#destroy'
end
+ collection do
+ resources :mutes, controller: 'statuses/mutes', only: :index
+ end
+
member do
get :context
get 'context/ancestors', to: 'statuses#ancestors'
@@ -386,21 +385,35 @@
namespace :timelines do
resource :home, only: :show, controller: :home
+ resource :following, only: :show, controller: :home
# resource :public, only: :show, controller: :public
resources :tag, only: :show
resources :list, only: :show
+
+ resources :group, only: :show do
+ resources :tags, only: :show, path: 'tags', controller: 'group_tag'
+ end
end
namespace :truth do
namespace :trending do
resources :truths, only: :index
+ resources :groups, only: :index
+ resources :group_tags, only: :show
end
namespace :admin do
- resources :accounts, only: [:index, :update]
+ resources :accounts, only: [:index, :update] do
+ post 'mfa/confirm/totp', to: 'accounts#confirm_totp'
+ end
scope :accounts do
+ get :blacklist, to: 'accounts#blacklist'
get :count, to: 'accounts#count'
+ get :email_domain_blocks, to: 'accounts#email_domain_blocks'
end
+ resources :email_domain_blocks, only: [:index, :create, :destroy]
+ resources :marketing_notifications, only: [:create]
+ resources :media_attachments, only: [:destroy]
end
scope :password_reset do
@@ -411,8 +424,96 @@
scope :email do
get :confirm, to: 'emails#email_confirm'
end
+
+ namespace :carousels do
+ resources :avatars, only: [:index] do
+ post :seen, on: :collection
+ end
+ resources :groups, only: [:index] do
+ post :seen, on: :collection
+ end
+ resources :suggestions, only: [:index]
+ get 'avatars/accounts/:account_id/statuses', to: '/api/v1/accounts/statuses#index'
+
+ get 'tv', to: '/api/v1/tv/carousel#index'
+ post 'tv/seen', to: '/api/v1/tv/carousel#seen'
+ end
+
+ get '/ads', to: 'ads#index', as: :ads
+ get '/ads/impression', to: 'ads#impression', as: :ads_impression
+
+ namespace :ios_device_check do
+ resources :challenge, only: [:index]
+ resources :rate_limit, only: [:index]
+ resources :attest, only: [:create] do
+ post :baseline, on: :collection
+ post :by_key_id, on: :collection
+ end
+ resources :assert, only: [:create] do
+ post :resolve, on: :collection
+ end
+ end
+
+ namespace :android_device_check do
+ resources :challenge, only: [:create]
+ end
+
+ resources :chats, only: [:index, :create] do
+ scope module: :chats do
+ resources :messages, only: [:index, :create, :destroy]
+ end
+ end
+
+ namespace :policies do
+ get :pending
+ patch :accept, path: '/:policy_id/accept', to: 'policies/accept'
+ end
+
+ resources :oauth_tokens, only: [:index, :destroy]
+
+ namespace :suggestions do
+ resources :groups, only: [:index, :destroy]
+
+ namespace :follows do
+ post :create, path: ':account_id', to: 'suggestions/follows'
+ end
+
+ namespace :statuses do
+ post :create, path: ':account_id', to: 'suggestions/statuses'
+ end
+ end
+
+ resources(:videos, only: :show)
end
+ namespace :pleroma do
+ namespace :chats do
+ post :by_account_id, path: 'by-account-id/:account_id'
+ get :by_account_id, path: 'by-account-id/:account_id', to: '/api/v1/pleroma/chats#get_by_account_id'
+ get 'silences', to: 'silences#index'
+ get 'sync'
+ get 'events', to: 'events#index'
+ get 'search', to: 'search#index'
+ get 'search/messages', to: 'search#search_messages'
+ get 'search/previews', to: 'search#search_previews'
+ end
+
+ resources :chats, only: [:index, :destroy, :show, :update] do
+ post 'read', to: 'chats#mark_read'
+ post 'accept', to: 'chats#accept'
+
+ scope module: :chats do
+ get 'sync', to: 'messages#sync'
+ resources :messages, only: [:index, :destroy, :create, :show] do
+ resources :reactions, only: [:show, :create, :destroy], param: :emoji
+ end
+ post 'silences', to: 'silences#create'
+ delete 'silences', to: 'silences#destroy'
+ get 'silences', to: 'silences#show'
+ end
+ end
+ end
+
resources :streaming, only: [:index]
resources :custom_emojis, only: [:index]
resources :suggestions, only: [:index, :destroy]
@@ -452,16 +553,16 @@
end
end
- resources :media, only: [:create, :update, :show]
- resources :blocks, only: [:index]
- resources :mutes, only: [:index]
- resources :favourites, only: [:index]
- resources :bookmarks, only: [:index]
- resources :reports, only: [:create]
- resources :trends, only: [:index]
- resources :filters, only: [:index, :create, :show, :update, :destroy]
+ resources :media, only: [:create, :update, :show]
+ resources :blocks, only: [:index]
+ resources :mutes, only: [:index]
+ resources :favourites, only: [:index]
+ resources :bookmarks, only: [:index]
+ resources :reports, only: [:create]
+ resources :trends, only: [:index]
+ resources :filters, only: [:index, :create, :show, :update, :destroy]
resources :endorsements, only: [:index]
- resources :markers, only: [:index, :create]
+ resources :markers, only: [:index, :create]
namespace :apps do
get :verify_credentials, to: 'credentials#show'
@@ -534,6 +635,54 @@
resource :accounts, only: [:show, :create, :destroy], controller: 'lists/accounts'
end
+ namespace :groups do
+ resources :relationships, only: [:index]
+ resources :tags, only: [:index]
+ end
+
+ get '/groups/mutes', to: 'groups/mutes#index'
+
+ resources :groups, only: [:index, :create, :show, :update, :destroy] do
+ post :mute, to: 'groups/mutes#create'
+ post :unmute, to: 'groups/mutes#destroy'
+
+ resources :memberships, only: [:index], controller: 'groups/memberships'
+
+ resources :membership_requests, only: [:index], controller: 'groups/membership_requests' do
+ member do
+ post :authorize, to: 'groups/membership_requests#accept'
+ post :reject
+ end
+ post 'resolve', on: :collection
+ end
+
+ resources :statuses, only: [:destroy], controller: 'groups/statuses' do
+ resource :pin, only: :create, controller: 'groups/statuses/pins'
+ post :unpin, to: 'groups/statuses/pins#destroy'
+ end
+
+ resource :blocks, only: [:show, :create, :destroy], controller: 'groups/blocks'
+
+ resources :tags, only: :update, controller: 'groups/tags'
+
+ member do
+ post :join
+ post :leave
+ post :promote
+ post :demote
+ end
+
+ collection do
+ get :search
+ get :lookup
+ get :validate
+ end
+ end
+
+ resources :tags, only: [:show] do
+ resources :groups, only: :index, controller: 'tags/groups'
+ end
+
namespace :featured_tags do
get :suggestions, to: 'suggestions#index'
end
@@ -548,20 +697,35 @@
resource :subscription, only: [:create, :show, :update, :destroy]
end
+ namespace :tv do
+ resources :channels, only: :index
+ get 'accounts/:id/status', to: 'accounts#show'
+ get 'epg/:name', to: 'programme_guides#show'
+ get 'channels/:id/guide', to: 'guide#show'
+ put 'channels/:id/remind', to: 'program_reminder#update'
+ delete 'channels/:id/remind', to: 'program_reminder#destroy'
+ end
+
get '/stats', to: 'admin#stats'
namespace :admin do
resources :statuses, only: [:index, :show] do
- post :sensitize
post :desensitize
- post :undiscard
post :discard
+ post :privatize
+ post :publicize
+ post :sensitize
+ post :undiscard
end
resources :accounts, only: [:index, :show, :create, :update, :destroy] do
resources :follows, only: [:show], param: :target_account_id, controller: 'accounts/follows'
+ resources :statuses, only: [:index], controller: 'accounts/statuses'
+ resources :webauthn_credentials, only: [:index], controller: 'accounts/webauthn_credentials'
+
collection do
post :bulk_approve
end
+
member do
post :enable
post :unsensitive
@@ -577,6 +741,13 @@
resource :action, only: [:create], controller: 'account_actions'
end
+ post '/accounts/bulk_action', to: 'bulk_account_actions#create'
+
+ resources :groups, only: [:index, :show, :update, :destroy] do
+ get :search, on: :collection
+ resources :statuses, only: [:index], controller: 'groups/statuses'
+ end
+
resources :reports, only: [:index, :show] do
member do
resources :moderation_records, only: [:index]
@@ -587,16 +758,119 @@
end
end
- resources :trending_statuses, only: [:index, :update, :destroy]
+ resources :trending_statuses, only: :index do
+ member do
+ put :include
+ put :exclude
+ end
+
+ collection do
+ resources :settings, param: :name, controller: 'trending_statuses/settings', only: [:index, :update]
+ end
+
+ collection do
+ resources :expressions, controller: 'trending_statuses/expressions', only: [:index, :create, :update, :destroy]
+ end
+ end
+
+ resources :trending_tags, only: [:index, :update]
+
+ resources :trending_groups, only: :index do
+ member do
+ put :include
+ put :exclude
+ end
+
+ get :excluded, on: :collection
+ end
+
+ resources :chat_messages, only: [:show, :destroy]
+
+ resources :policies, only: [:index, :create, :destroy]
+
+ namespace :truth do
+ resources :interactive_ads, only: :create
+ namespace :suggestions do
+ resources :groups, only: [:index, :show, :create, :destroy]
+ end
+
+ namespace :android_device_check do
+ resources :integrity, only: [:create]
+ end
+
+ namespace :ios_device_check do
+ resources :attest, only: [:create]
+ end
+ end
+
+ namespace :tv do
+ resources :sessions, only: :index
+ end
+
+ resources :tags, only: [:index, :update]
+ resources :registrations, only: [:create]
+ resources :links, only: [:update]
end
+
+ resources :feeds, only: [
+ :index,
+ # :create,
+ :show,
+ :update,
+ # :destroy
+ ] do
+ member do
+ post 'accounts/:account_id', to: 'feeds#add_account'
+ delete 'accounts/:account_id', to: 'feeds#remove_account'
+ patch :seen, to: 'feeds#seen'
+ end
+ end
+
+ namespace :recommendations do
+ namespace :accounts do
+ resources :suppressions, only: [:create, :destroy]
+ end
+ namespace :groups do
+ resources :suppressions, only: [:create, :destroy]
+ end
+ end
+
+ namespace :verify_sms do
+ resources :countries, only: :index
+ end
+
+ namespace :push_notifications do
+ post '/:mark_id/mark', to: 'analytics#mark', as: :analytics_mark
+ end
end
namespace :v2 do
resources :media, only: [:create]
get '/search', to: 'search#index', as: :search
resources :suggestions, only: [:index, :destroy]
+
+ namespace :pleroma do
+ namespace :chats do
+ get 'events', to: 'events#index'
+ end
+ end
+
+ resources :statuses, only: [:show] do
+ member do
+ get 'context/ancestors', to: 'statuses#ancestors', as: 'ancestors'
+ get 'context/descendants', to: 'statuses#descendants', as: 'descendants'
+ end
+ end
+
+ resources :feeds, only: [:index]
end
+ namespace :v4 do
+ namespace :truth do
+ get '/ads', to: 'ads#index'
+ end
+ end
+
namespace :web do
resource :settings, only: [:update]
resource :embed, only: [:create]
@@ -606,13 +880,35 @@
end
end
end
+
+ namespace :mock do
+ get '/feeds', to: 'feeds#index'
+ post '/feeds', to: 'feeds#create'
+ get '/feeds/:id', to: 'feeds#show'
+ patch '/feeds/:id', to: 'feeds#update'
+ delete '/feeds/:id', to: 'feeds#destroy'
+ put '/feeds/sort', to: 'feeds#sort'
+ post '/feeds/:id/accounts/:account_id', to: 'feeds#add_account'
+ delete '/feeds/:id/accounts/:account_id', to: 'feeds#remove_account'
+ post '/feeds/groups/:group_id/unmute', to: 'feeds#unmute_group'
+ post '/feeds/groups/:group_id/mute', to: 'feeds#mute_group'
+ end
end
+ get '/api/v2/pleroma/chats', to: 'api/v1/pleroma/chats#index'
+ get '/api/v1/truth/trends/groups', to: 'api/v1/truth/trending/groups#index'
+ get '/api/v1/truth/trends/groups/:id/tags', to: 'api/v1/truth/trending/group_tags#show', as: :truth_trends_groups
+
+ get '/api/oauth_tokens', to: 'api/v1/truth/oauth_tokens#index'
+ delete '/api/oauth_tokens/:id', to: 'api/v1/truth/oauth_tokens#destroy'
+
+ get '/api/v1/trends/statuses', to: 'api/v1/truth/trending/truths#index'
+
get '/web/(*any)', to: 'home#index', as: :web
- get '/about', to: 'about#show'
- get '/about/more', to: 'about#more'
- get '/terms', to: 'about#terms'
+ get '/about', to: 'about#show'
+ get '/about/more', to: 'about#more'
+ get '/terms', to: 'about#terms'
match '/', via: [:post, :put, :patch, :delete], to: 'application#raise_not_found', format: false
match '*unmatched_route', via: :all, to: 'application#raise_not_found', format: false
diff -ru truth-old/opensource/config/settings.yml truth-new/opensource/config/settings.yml
--- truth-old/opensource/config/settings.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/settings.yml 2024-04-01 14:59:13
@@ -61,6 +61,7 @@
- administrator
- mod
- moderator
+ - amtrak
disallowed_hashtags: # space separated string or list of hashtags without the hash
bootstrap_timeline_accounts: ''
activity_api_enabled: true
diff -ru truth-old/opensource/config/sidekiq.yml truth-new/opensource/config/sidekiq.yml
--- truth-old/opensource/config/sidekiq.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/sidekiq.yml 2024-04-12 09:09:08
@@ -7,6 +7,9 @@
- [mailers, 2]
- [pull]
- [chewy]
+ - [removal]
+ - [reblog-removal]
+ - [shared]
:scheduler:
:listened_queues_only: true
:schedule:
@@ -14,18 +17,10 @@
every: '5m'
class: Scheduler::ScheduledStatusesScheduler
queue: scheduler
- trending_tags_scheduler:
- every: '5m'
- class: Scheduler::TrendingTagsScheduler
- queue: scheduler
media_cleanup_scheduler:
cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
class: Scheduler::MediaCleanupScheduler
queue: scheduler
- feed_cleanup_scheduler:
- cron: '<%= Random.rand(0..59) %> <%= Random.rand(0..2) %> * * *'
- class: Scheduler::FeedCleanupScheduler
- queue: scheduler
follow_recommendations_scheduler:
cron: '<%= Random.rand(0..59) %> <%= Random.rand(6..9) %> * * *'
class: Scheduler::FollowRecommendationsScheduler
@@ -46,10 +41,6 @@
cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
class: Scheduler::BackupCleanupScheduler
queue: scheduler
- pghero_scheduler:
- cron: '0 0 * * *'
- class: Scheduler::PgheroScheduler
- queue: scheduler
instance_refresh_scheduler:
cron: '0 * * * *'
class: Scheduler::InstanceRefreshScheduler
@@ -57,4 +48,20 @@
users_approval_scheduler:
every: '5m'
class: Scheduler::UsersApprovalScheduler
+ queue: scheduler
+ refresh_receipt_scheduler:
+ cron: '0 0 * * *'
+ class: Scheduler::RefreshReceiptScheduler
+ queue: scheduler
+ tv_create_program_records_scheduler:
+ every: '10m'
+ class: Scheduler::TvCreateProgramRecordsScheduler
+ queue: scheduler
+ tv_refetch_channels_list_scheduler:
+ cron: '0 1 * * *'
+ class: Scheduler::TvRefetchChannelsListScheduler
+ queue: scheduler
+ device_verification_cleanup_scheduler:
+ cron: '0 2 * * *'
+ class: Scheduler::DeviceVerificationCleanupWorker
queue: scheduler
diff -ru truth-old/opensource/config/webpacker.yml truth-new/opensource/config/webpacker.yml
--- truth-old/opensource/config/webpacker.yml 2022-06-08 09:15:38
+++ truth-new/opensource/config/webpacker.yml 2024-04-01 14:59:13
@@ -50,7 +50,7 @@
development:
<<: *default
- compile: true
+ compile: false
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
diff -ru truth-old/opensource/db/schema.rb truth-new/opensource/db/schema.rb
--- truth-old/opensource/db/schema.rb 2022-06-08 09:15:38
+++ truth-new/opensource/db/schema.rb 2024-04-01 14:59:13
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2022_05_25_140644) do
+ActiveRecord::Schema.define(version: 2022_06_10_102254) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -181,8 +181,10 @@
t.text "location", default: "", null: false
t.text "website", default: "", null: false
t.boolean "whale", default: false
+ t.integer "interactions_score"
t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
t.index "lower((username)::text), COALESCE(lower((domain)::text), ''::text)", name: "index_accounts_on_username_and_domain_lower", unique: true
+ t.index ["interactions_score"], name: "index_accounts_on_interactions_score"
t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id"
t.index ["uri"], name: "index_accounts_on_uri"
t.index ["url"], name: "index_accounts_on_url"
@@ -280,6 +282,32 @@
t.index ["reference_account_id"], name: "index_canonical_email_blocks_on_reference_account_id"
end
+ create_table "chat_accounts", force: :cascade do |t|
+ t.bigint "account_id"
+ t.bigint "chat_id"
+ t.index ["account_id"], name: "index_chat_accounts_on_account_id"
+ t.index ["chat_id"], name: "index_chat_accounts_on_chat_id"
+ end
+
+ create_table "chat_messages", force: :cascade do |t|
+ t.bigint "account_id"
+ t.bigint "chat_id"
+ t.text "content"
+ t.boolean "unread", default: true, null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["account_id"], name: "index_chat_messages_on_account_id"
+ t.index ["chat_id"], name: "index_chat_messages_on_chat_id"
+ end
+
+ create_table "chats", force: :cascade do |t|
+ t.integer "unread"
+ t.bigint "created_by_account"
+ t.datetime "last_message"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ end
+
create_table "conversation_mutes", force: :cascade do |t|
t.bigint "