Compare commits

...

139 Commits

Author SHA1 Message Date
syuilo
3043b5256d Merge pull request #12060 from misskey-dev/develop
Release: 2023.10.2
2023-10-21 14:18:53 +09:00
syuilo
b397a72b28 New Crowdin updates (#12094)
* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Uyghur)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Croatian)

* New translations ja-jp.yml (Haitian Creole)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Chinese Simplified)
2023-10-21 13:39:12 +09:00
syuilo
3b9983cfc2 2023.10.2 2023-10-21 13:38:57 +09:00
woxtu
e6873fb259 Switch avatar images that depend on the animation setting (#12097) 2023-10-21 13:31:16 +09:00
Natsuki Ikeguchi
f4970c7d2f fix(frontend): Use opening quote in notifications (#12082)
Signed-off-by: Natsuki Ikeguchi <me@s6n.jp>
2023-10-21 07:54:28 +09:00
shiosyakeyakini
6d6ddbc35e fix(backend) api/iの未読のお知らせにcreatedAtを追加するように修正 (#12092)
Co-authored-by: sorairo <sorairo@shiosyakeyakini.info>
2023-10-21 07:53:57 +09:00
taichan
3e5c55c14e chore: Pull Request時にapi.jsonの差分を表示するActions workflow (#12090)
* chore: Pull Request時にapi.jsonのdiffを出力するworkflow

* refactor: job names

* fix: set repository to get api diff

* chore: set permission to workflow

* set sleep 30s (shorter)

* chore: set label of diff

* chore: more attempts to fetch misskey

* chore: add full diff output of api.js

* chore: save full-diff to Artifact

* chore: add message to download diff Artifact
2023-10-21 07:45:47 +09:00
syuilo
42a3489bcb update deps 2023-10-20 15:46:12 +09:00
syuilo
df957f7afe update test 2023-10-20 13:07:08 +09:00
syuilo
b394328eb1 fix(backend): フォロ限へのリプのリプ元が(非公開)の表示のまま
Fix #12083
2023-10-20 11:58:09 +09:00
syuilo
21986a2168 enhance(frontend): 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく 2023-10-20 11:51:01 +09:00
syuilo
18af290b18 chore: tweak MkNotifications 2023-10-20 11:33:33 +09:00
syuilo
d962ea3889 add timeline tests 2023-10-20 08:15:31 +09:00
syuilo
683b71fc7e add timeline tests 2023-10-20 08:13:20 +09:00
syuilo
216b20d2db fix(backend): 自分のフォロワー限定ノートがWebsoketに乗ってこない
Fix #12081
2023-10-20 08:02:32 +09:00
syuilo
6ff5bfd2bc clean up 2023-10-20 07:48:31 +09:00
syuilo
4a7c6e261a fix(backend): 管理者権限のロールを持っていても一部のAPIが使用できないことがある問題を修正 2023-10-19 20:47:23 +09:00
syuilo
e5598da7a2 disable cypress widgets tests 2023-10-19 20:22:24 +09:00
syuilo
cc256f117e update deps 2023-10-19 19:51:59 +09:00
syuilo
d9241df84d New Crowdin updates (#12070)
* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Chinese Traditional)
2023-10-19 19:47:36 +09:00
syuilo
84a9e4a27b Update CHANGELOG.md 2023-10-19 19:47:25 +09:00
atsuchan
7b361224f8 fix(frontend): Recieve Unrenote on streaming (#12079)
* fix(frontend): Recieve Unrenote

表示しているリノートがリノート解除されたらストリーミングで受信してすぐに消えるようにする

* fix(frontend): Recieve Unrenote lint fixing

* fix(frontend): Recieve Unrenote Decapture

Decapture忘れてたー
2023-10-19 19:36:18 +09:00
dependabot[bot]
3c3d05ba2e chore(deps): bump actions/checkout from 4.1.0 to 4.1.1 (#12062)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-19 19:35:13 +09:00
anatawa12
991fa054a6 chore: STLのdb fallbackでwithRepliesがtrueのときにすべてのリプライを除外しないように (#12075)
MiFollowingを見るのは実装コストが高いため現状実装していない

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-10-19 19:34:52 +09:00
A.Yamamoto
9afcdd10ed UserLite.nameにnullが入りうるのを型で明示 (#12073)
* UserLite.nameにnullが入りうるのを型で明示

* ドキュメントの追従
2023-10-19 19:33:45 +09:00
syuilo
721cbe085b fix(frontend): fix of 30efd932a5 2023-10-19 17:42:19 +09:00
anatawa12
93d3501c90 fix: replies are included even if withReplies = false in local timeline (#12074) 2023-10-19 17:29:09 +09:00
syuilo
431d8c7802 fix(backend): Redisがからのときにhybrid-timelineにwithReplies = trueでアクセスするとSQLのシンタックスエラーになる
Fix #12064
2023-10-19 16:22:19 +09:00
syuilo
f85a655915 2023.10.2-beta.2 2023-10-19 11:43:28 +09:00
syuilo
5891adc5cf Update CHANGELOG.md 2023-10-19 11:42:52 +09:00
syuilo
30efd932a5 enhance: nyaizeはクライアントで表示時に行うように
Resolve #12030
2023-10-19 11:42:17 +09:00
syuilo
ec45db7870 refactor and perf tweak 2023-10-19 11:19:42 +09:00
syuilo
428d39a460 chore: disable debug log of fastify 2023-10-19 11:18:17 +09:00
syuilo
f9549e1f1b fix(backend): fix of 1671575d5d 2023-10-19 11:17:59 +09:00
syuilo
1671575d5d perf(backend): ノートのリアクション情報をキャッシュすることでDBへのクエリを削減 2023-10-19 09:20:19 +09:00
syuilo
4d1d25e02f perf(backend): improve my reaction population performance 2023-10-19 08:07:22 +09:00
syuilo
2dfbf97db4 refactor 2023-10-19 07:59:58 +09:00
syuilo
fcc4864080 perf(backend): reduce needless populateMyReaction calls 2023-10-19 07:56:25 +09:00
syuilo
de6e15e36d Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-10-18 17:41:56 +09:00
syuilo
01f294bd91 Update CHANGELOG.md 2023-10-18 17:41:48 +09:00
syuilo
92b49b6ebb New Crowdin updates (#12036)
* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)
2023-10-18 17:41:32 +09:00
syuilo
524b669f5a 2023.10.2-beta.1 2023-10-18 17:22:13 +09:00
syuilo
5b244cc6eb update test 2023-10-18 17:20:43 +09:00
syuilo
ae1e276085 fix of 6cc02fee99 2023-10-18 17:17:56 +09:00
syuilo
6b5ee43800 enhance(backend): improve fanout tl for stl 2023-10-18 15:51:50 +09:00
syuilo
6b7efb6f1d fix(backend): fix admin/get-user-ips error 2023-10-18 15:32:35 +09:00
syuilo
3c5bcdd7b3 Update CHANGELOG.md 2023-10-18 12:27:15 +09:00
syuilo
6cc02fee99 enhance(backend): improve fanout tl
Resolve #11958
Resolve #12061
2023-10-18 12:26:16 +09:00
syuilo
2a88d8ee2d refactor(backend): rename service 2023-10-18 12:07:18 +09:00
syuilo
52a82ac193 Update about-misskey.vue 2023-10-18 10:07:21 +09:00
nryeouo
d82b3a08e2 Update ja-JP.yml (#12053)
ポスト
2023-10-18 10:01:50 +09:00
taichan
f6e1ee1d40 users/notesでDBから参照した際にチャンネル投稿のみ取得される問題を修正 (#12056)
* fix(backend): ユーザーのノート取得でDBにfallbackした際にチャンネルノートのみになる

* Update CHANGELOG

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-10-18 09:55:15 +09:00
woxtu
0bddd0ceae Remove unnecessary nullish coalescing (#12058) 2023-10-18 09:54:18 +09:00
かっこかり
53099cad5a fix: admin/update-meta が保存できない (#12057)
* Fix: invalid param impressumUrl

* Update CHANGELOG.md
2023-10-18 09:48:49 +09:00
syuilo
145c5c8b2b update deps 2023-10-18 09:43:59 +09:00
anatawa12
5a3c6575dd 新規にフォローした人のwithRepliesをtrueにする機能を追加 (#12048)
* feat: add defaultWithReplies to MiUser

* feat: use defaultWithReplies when creating MiFollowing

* feat: update defaultWithReplies from API

* feat: return defaultWithReplies as a part of $i

* feat(frontend): configure defaultWithReplies

* docs(changelog): 新規にフォローした人のをデフォルトでTL二追加できるように

* fix: typo

* style: fix lint failure

* chore: improve UI text

* chore: make optional params of  UserFollowingService.follow() object

* chore: UserFollowingService.follow() accept withReplies

* chore: add withReplies to MiFollowRequest

* chore: process withReplies for follow request

* feat: accept withReplies on 'following/create' endpoint

* feat: store defaultWithReplies in client store

* Revert "feat: return defaultWithReplies as a part of $i"

This reverts commit f2cc4fe6

* Revert "feat: update defaultWithReplies from API"

This reverts commit 95e3cee6

* Revert "feat: add defaultWithReplies to MiUser"

This reverts commit 9f5ab14d70.

* feat: configuring withReplies in import-following

* feat(frontend): configure withReplies

* fix(frontend): incorrectly showRepliesToOthersInTimeline can be shown

* fix(backend): withReplies of following/create not working

* fix(frontend): importFollowing error

* fix: withReplies is not working with follow import

* fix(frontend): use v-model

* style: fix lint

---------

Co-authored-by: Sayamame-beans <61457993+sayamame-beans@users.noreply.github.com>
Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
2023-10-17 20:56:17 +09:00
syuilo
e9db0680c4 update deps 2023-10-17 09:04:17 +09:00
syuilo
5686e1d2d0 refactor 2023-10-17 09:00:42 +09:00
syuilo
9338ab461e update deps 2023-10-16 20:13:05 +09:00
syuilo
7e4562d50a Update CHANGELOG.md 2023-10-16 20:12:06 +09:00
まっちゃてぃー
5efd01ba70 feat: サーバーサイレンス機能を追加 (#12031)
* feat : サーバーサイレンスを追加

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update locale

* Update instance-info.vue

* update misskey-js.api.md

* lint fix

* migration fix

* 既存のものを使うように

* fix

* 色々直した

* Update packages/frontend/src/pages/admin/instance-block.vue

* Update packages/frontend/src/pages/admin/instance-block.vue

* Update packages/frontend/src/components/MkInstanceCardMini.vue

* Update packages/backend/src/core/entities/InstanceEntityService.ts

* Update packages/backend/src/core/entities/InstanceEntityService.ts

* Update packages/backend/src/core/entities/InstanceEntityService.ts

* Update packages/backend/src/core/UserFollowingService.ts

* Update packages/backend/src/core/UserFollowingService.ts

* fix: サイレンスされてるサーバーからの投稿は全部ホームにする

* fix: undefinedでfalseを返すようにした

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-10-16 20:11:27 +09:00
syuilo
1966876320 fix test 2023-10-16 17:42:33 +09:00
syuilo
3ebed5aa3e feat: local-only antenna
Resolve #11869
2023-10-16 15:06:00 +09:00
syuilo
6a321ba340 fix test 2023-10-16 14:35:44 +09:00
syuilo
34d1b463a4 fix tests 2023-10-16 12:58:17 +09:00
syuilo
24437a04d4 fix of 1fa1d31696 2023-10-16 12:00:56 +09:00
syuilo
1fa1d31696 perf(backend): createdAtをidから取得するように & 無駄なDateインスタンスの生成を避けるように 2023-10-16 10:45:22 +09:00
syuilo
15706c8c2b Update CHANGELOG.md 2023-10-15 16:25:42 +09:00
syuilo
5117818f6f Update CHANGELOG.md 2023-10-15 10:37:03 +09:00
syuilo
3f4ee98405 perf(backend): improve streaming api performance (#12033)
* wip

* Update NoteEntityService.ts

* wip

* wip

* wip

* wip
2023-10-15 10:36:22 +09:00
syuilo
329830e2c3 perf(backend): tweak populateMyReaction 2023-10-14 10:05:44 +09:00
syuilo
061e389340 perf(backend): nyaizeを投稿時にやる
Resolve #12030
2023-10-14 10:04:13 +09:00
syuilo
096fa16c4c enhance(frontend): TLの返信表示オプションを記憶するように
Resolve #12016
2023-10-13 16:49:56 +09:00
syuilo
04908bc90c .js 2023-10-13 16:42:57 +09:00
syuilo
7e7138c0eb Merge pull request #12011 from misskey-dev/develop
Release: 2023.10.1
2023-10-12 09:21:04 +09:00
syuilo
87c9870447 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-10-12 09:20:41 +09:00
syuilo
34eeccf908 2023.10.1 2023-10-12 09:20:33 +09:00
syuilo
c2e177e37a New Crowdin updates (#12013)
* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)
2023-10-12 09:20:25 +09:00
syuilo
7b6b3ad821 update test 2023-10-11 18:25:02 +09:00
syuilo
7132958948 fix(frontend): 絵文字ピッカーで横に長いカスタム絵文字が見切れる問題を修正 2023-10-11 17:58:40 +09:00
syuilo
a26d9ea132 enhance(backend): LTLでフォローしているユーザーからの自分への返信が含まれるように 2023-10-11 17:29:24 +09:00
syuilo
566cb35370 update test 2023-10-11 14:30:18 +09:00
syuilo
cf3624a54f fix(backend): users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正 2023-10-11 12:26:07 +09:00
syuilo
8a302a9af4 Update CHANGELOG.md
Fix #12003
2023-10-11 11:44:12 +09:00
syuilo
7a8d5e5840 enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に
Resolve #12001
2023-10-11 10:15:44 +09:00
syuilo
1f0c27edf2 fix(backend): フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 2023-10-11 08:18:00 +09:00
syuilo
26b7112b20 add tests 2023-10-11 08:02:29 +09:00
syuilo
f964ef163b Merge pull request #11963 from misskey-dev/develop
Release: 2023.10.0
2023-10-10 20:40:13 +09:00
syuilo
854ac95511 fix(backend): センシティブ設定されたチャンネルの投稿をusers/notesで返さないように 2023-10-10 20:06:02 +09:00
syuilo
51b6a012a5 fix(frontend): ユーザープロフィールページでセンシティブなメディアが隠されない問題を修正 2023-10-10 19:49:25 +09:00
syuilo
085bcf24da Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-10-10 19:08:02 +09:00
syuilo
66940d6cf1 fix(backend): channels/timelineでミュートが効かない問題を修正 2023-10-10 19:07:59 +09:00
syuilo
61ff98c8dd New Crowdin updates (#12000)
* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)
2023-10-10 19:03:21 +09:00
syuilo
43fe0cfda8 Update CHANGELOG.md 2023-10-10 19:03:01 +09:00
syuilo
57b794edfb New Crowdin updates (#11999)
* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)
2023-10-10 18:14:09 +09:00
syuilo
47de264478 2023.10.0 2023-10-10 18:13:57 +09:00
syuilo
373c2af46a clean up 2023-10-10 18:11:58 +09:00
syuilo
f5e72f7d3e 🎨 CWボタンを大きく 2023-10-10 18:08:54 +09:00
syuilo
d81c833775 Update CHANGELOG.md 2023-10-10 16:34:22 +09:00
syuilo
cf6e53b2ac update deps 2023-10-10 16:26:48 +09:00
syuilo
9dd0f8c39b clean up 2023-10-10 16:25:06 +09:00
syuilo
d94380780f Update CHANGELOG.md 2023-10-10 10:47:11 +09:00
かっこかり
af1087aed4 Feat:「ファイルの詳細」ページを追加 (#11995)
* (add) ファイルビューア

* Update Changelog

* 既存のAPIを利用

* run api extratctor

* Change i18n

* (add) ページに関する説明を追加

* Update CHANGELOG

* (fix) design, classes
2023-10-10 10:43:43 +09:00
syuilo
9f33ce1cd0 fix of 0bb0c32908 2023-10-10 09:45:40 +09:00
syuilo
4eb9e50a36 2023.10.0-beta.15 2023-10-09 21:52:43 +09:00
syuilo
8ab3640291 fix of 0bb0c32908 2023-10-09 21:52:31 +09:00
syuilo
fc777be7bc 2023.10.0-beta.14 2023-10-09 21:23:18 +09:00
syuilo
edf847d966 fix of 0bb0c32908 2023-10-09 21:23:07 +09:00
syuilo
0e6cd577cc Merge pull request #11926 from misskey-dev/develop
* fix(backend): Redisに古いMisskeyバージョンのキャッシュが残っている場合の問題を修正

* Update CHANGELOG.md

* enhance(front)end: improve moderation log

* enhance: ノートの翻訳機能の利用可否をロールで設定可能に

Resolve #11923

* 2023.9.3

* 後方互換性の強化

* Update CHANGELOG.md

* fix test

* [ci skip] New Crowdin updates (#11922)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* feat: AiScriptでホストのアドレスを参照できる定数 (#11924)

* add HOST_URL

* Update CHANGELOG.md

* tweak

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

---------

Co-authored-by: FineArchs <133759614+FineArchs@users.noreply.github.com>
2023-09-30 09:40:00 +09:00
syuilo
7adc8fcaf5 Merge pull request #11920 from misskey-dev/develop
Release: 2023.9.2
2023-09-29 18:11:30 +09:00
syuilo
e57b536767 Merge pull request #11898 from misskey-dev/develop
2023.9.1
2023-09-25 17:12:28 +09:00
syuilo
f32915b515 Merge pull request #11874 from misskey-dev/develop
Release: 2023.9.0
2023-09-24 18:21:31 +09:00
syuilo
a8d45d4b0d Merge pull request #11384 from misskey-dev/develop
Release: 13.14.2
2023-07-27 13:00:14 +09:00
syuilo
4e24aff408 Merge pull request #11338 from misskey-dev/develop
Release: 13.14.1
2023-07-21 20:40:03 +09:00
syuilo
e64a81aa1d Merge pull request #11301 from misskey-dev/develop
Release: 13.14.0
2023-07-21 20:36:07 +09:00
syuilo
7093662ce5 Merge pull request #10990 from misskey-dev/develop
Release: 13.13.2
2023-06-13 16:46:01 +09:00
syuilo
32c741154d Merge pull request #10961 from misskey-dev/develop
Release: 13.13.1
2023-06-06 11:34:36 +09:00
syuilo
407a965c1d Merge pull request #10932 from misskey-dev/develop
Release: 13.13.0
2023-06-05 19:47:08 +09:00
syuilo
de6348e8a0 Merge pull request #10833 from misskey-dev/develop
* refactor(frontend): use css modules

* feat: 投稿したコンテンツのAIによる学習を軽減するオプションを追加

Resolve #10819

* enhance(backend): publicReactionsをデフォルトtrueに

* 念のためnoimageaiもつける

* add X-Robots-Tag: noai

* Update ja-JP.yml

* fix(frontend): ブラーエフェクトを有効にしている状態で高負荷になる問題を修正

* enhance(backend): graceful shutdown for job queue and refactor

* fix(backend): テスト時は一部のサービスを停止

* fix test

* New Crowdin updates (#10815)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* refactor

* bump

* refactor(frontend): use css module

* refactor(frontend): use css module

* delete unused component

* センシティブワードを正規表現、CWにも適用するように (#10688)

* cwにセンシティブが効いてない

* CWが無いときにTextを見るように

* 比較演算子間違えた

* とりあえずチェック

* 正規表現対応

* /test/giにも対応

* matchでしなくてもいいのでは感

* レビュー修正

* Update packages/backend/src/core/NoteCreateService.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* Update packages/backend/src/core/NoteCreateService.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* 修正

* wipかも

* wordsでスペース区切りのものできたかも

* なんか動いたかも

* test作成

* 文言の修正

* 修正

* note参照

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* Update CHANGELOG.md

* New Crowdin updates (#10823)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* ci: fix typo

* fix(frontend): より明確な説明にしたのとtypo修正

* fix typo

* fix(frontend): カラーバーがリプライには表示されないのを修正

* fix(frontend): チャンネル内の検索ボックスが挙動不審な問題を修正

Fix #10793

* enhance(backend): ノートのハッシュタグもMeilisearchに突っ込むように

今後ハッシュタグ検索とか実装するときのため

* feat(frontend): ユーザー指定ノート検索

* fix(frontend): fix retention chart rendering

* Update about-misskey.vue

* meta: Remove @rinsuki from reviewer-lottery (#10830)

* New Crowdin updates (#10824)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Thai)

* enhance(frontend): アカウント初期設定ウィザードにプライバシー設定を追加

* Update CHANGELOG.md

* fix(backend): ひとつのMeilisearchサーバーを複数のMisskeyサーバーで使えない問題を修正

* fix MkUserSetupDialog.Privacy.vue

* ci: skip non-Japanese locale on TurboSnap

* ci: notify on changes for push events

* ci: fix missing branch

* Update basic.cy.js

* [ci skip] New Crowdin updates (#10834)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Arabic)

* 🎨

* 🎨

* enhance(frontend): add retention line chart

* update deps

* refactor

* fix(frontend): Pageにおいて画像ブロックに画像を設定できない問題を修正

Fix #10837

---------

Co-authored-by: nenohi <kimutipartylove@gmail.com>
Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: rinsuki <428rinsuki+git@gmail.com>
2023-05-12 12:41:53 +09:00
syuilo
9ad57324db Merge pull request #10814 from misskey-dev/develop
Release: 13.12.1
2023-05-09 15:38:17 +09:00
syuilo
94690c835e Merge pull request #10774 from misskey-dev/develop
Release: 13.12.0
2023-05-09 09:17:34 +09:00
syuilo
c5d2dba28d Merge pull request #10608 from misskey-dev/develop
Release: 13.11.3
2023-04-13 12:18:07 +09:00
syuilo
272e0c874f Merge pull request #10606 from misskey-dev/EbiseLutica-patch-1
Update CHANGELOG.md
2023-04-13 08:35:14 +09:00
Ebise Lutica
d429f810a9 Update CHANGELOG.md 2023-04-13 00:31:22 +09:00
syuilo
75b28d6782 Merge pull request #10578 from misskey-dev/develop
Release: 13.11.2
2023-04-11 15:51:07 +09:00
syuilo
8b1362ab03 Merge pull request #10543 from misskey-dev/develop
Release: 13.11.1
2023-04-09 10:29:36 +09:00
syuilo
a096f621cf Merge pull request #10506 from misskey-dev/develop
13.11.0
2023-04-08 21:27:21 +09:00
syuilo
f54a9542bb Merge pull request #10402 from misskey-dev/develop
Release: 13.10.3
2023-03-25 08:36:41 +09:00
syuilo
a52bbc7c8d Merge pull request #10388 from misskey-dev/develop
Release: 13.10.2
2023-03-22 18:47:10 +09:00
syuilo
59768bdf3f Merge pull request #10383 from misskey-dev/develop
Release: 13.10.1
2023-03-22 16:30:36 +09:00
syuilo
1e67e9c661 Merge pull request #10342 from misskey-dev/develop
Release: 13.10.0
2023-03-22 09:55:38 +09:00
syuilo
ae517a99a7 Merge pull request #10218 from misskey-dev/develop
Release: 13.9.2
2023-03-06 11:54:12 +09:00
syuilo
b23a9b1a88 Merge pull request #10181 from misskey-dev/develop
Release: 13.9.1
2023-03-03 20:56:50 +09:00
syuilo
5bd68aa3e0 Merge pull request #10177 from misskey-dev/develop
Release: 13.9.0
2023-03-03 15:35:40 +09:00
syuilo
647ce174b3 Merge pull request #10112 from misskey-dev/develop
Release: 13.8.1
2023-02-26 20:57:13 +09:00
syuilo
02c8fd9de5 Merge pull request #10108 from misskey-dev/develop
* Add dialog to remove follower (#9718)

* update PULL_REQUEST_TEMPLATE

* 起動時にRedisの疎通確認を行う (#9832)

* 起動時にRedisの疎通確認を行う

* check:connectをstart内に移動

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>

* Pass `--detectOpenHandles` to Jest (#9895)

Co-authored-by: tamaina <tamaina@hotmail.co.jp>

* enhance(client): MkUrlPreviewの閉じるボタンを見やすく (#9913)

Co-authored-by: tamaina <tamaina@hotmail.co.jp>

* test(backend): restore ap-request tests (#9997)

Co-authored-by: tamaina <tamaina@hotmail.co.jp>

* fix/refaftor(client): MkTime.vueの変更 (#10061)

* fix(client): MkTime.timeにstringでもDateでない値が入った場合、?を表示

* fix(client): MkTimeを改良

* numberを許容

* falsyな値もとる

* 不明

* ありません

* fix

* fix(server): notes/createで、fileIdsと見つかったファイルの数が異なる場合はエラーにする (#9911)

* fix(server): notes/createで、fileIdsと見つかったファイルの数が異なる場合はエラーにする

* NO_SUCH_FILE

* Update codecov.yml

* Update apple-touch-icon.png

* デプロイされているプレビュー環境がない場合はプレビュー環境を削除しないようにする (#10062)

* デプロイされているプレビュー環境がない場合はDestroy preview environmentを実行しないようにする

* CIがない場合の処理追加

* enhance(client): improve clip menu ux

* 未知のユーザーが deleteActor されたら処理をスキップする (#10067)

* fix(client): Android ChromeでPWAとしてインストールできない問題を修正 (#10069)

* fix(client): Android ChromeでPWAとしてインストールできない問題を修正

* 順番関係ある?

* Windows環境でswcを使うと正常にビルドができない問題の修正 (#10074)

* Update @swc/core to v1.3.36

* Update CHANGELOG.md

* Update CHANGELOG.md

* バックグラウンドで一定時間経過したらページネーションのアイテム更新をしない (#10053)

* 🎨

* feat: 2つの検索画面の統合 (#9949) (#10038)

* feat: 検索画面の UI を統一

* fix: エラーの修正

* add: changelog

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* enhance(client): ノートメニューからユーザーメニューを開けるように

Resolve #10019

* enhance(client): renoteした際の表示を改善

Resolve #10078

* Update CHANGELOG.md

* enhance(client): tweak contextmenu position calculation

* 🎨

* 🎨

* feat: in-channel featured note

Resolve #9938

* refactor(frontend): fix eslint error (#10084)

* Simplify search.vue (remove dead code) (#10088)

* Simplify search.vue

This is already handled by the code above it, no need to handle it twice

* Remove unused imports

* Update about-misskey.vue

* test(server): add validation test of api:notes/create (#10090)

* fix(server): notes/createのバリデーションが効いていない
Fix #10079

Co-Authored-By: mei23 <m@m544.net>

* anyOf内にバリデーションを書いても最初の一つしかチェックされない

* ✌️

* wip

* wip

* ✌️

* RequiredProp

* Revert "RequiredProp"

This reverts commit 7469390011.

* add api:notes/create

* fix lint

* text

* ✌️

* improve readability

---------

Co-authored-by: mei23 <m@m544.net>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* New Crowdin updates (#10059)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Romanian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* enhance(client): improve user menu ux

* enhance(client): photoswipe 表示時に戻る操作をしても前の画面に戻らないように (#10098)

* enhance(client): photoswipe 表示時に戻る操作をしても前の画面に戻らないように

* add: changelog

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* enhance(client): メニューの「もっと」からインスタンス情報を見れるように

* [Fix] fixed an typo in error message (#10102)

* Update codecov.yml

* Update CHANGELOG.md

* fix(server): エラーのスタックトレースは返さないように

Fix #10064

* [chore]Editorconfig: ymlに加えてyamlファイルに対しても同じ規約を適用する (#10081)

* Added yaml file in addition to yml file, in editorconfig

* Applied editorconfig for pnpm-workspace.yaml

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* update deps

* ホームタイムラインの読み込みでクエリタイムアウトになるのを修正する (#10106)

* refactor

* New translations ja-JP.yml (French) (#10103)

* Update CHANGELOG.md

* 13.8.0

---------

Co-authored-by: atsuchan <83960488+atsu1125@users.noreply.github.com>
Co-authored-by: Masaya Suzuki <15100604+massongit@users.noreply.github.com>
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com>
Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
Co-authored-by: xianon <xianon@hotmail.co.jp>
Co-authored-by: kabo2468 <28654659+kabo2468@users.noreply.github.com>
Co-authored-by: YS <47836716+yszkst@users.noreply.github.com>
Co-authored-by: Khsmty <me@khsmty.com>
Co-authored-by: Soni L <EnderMoneyMod@gmail.com>
Co-authored-by: mei23 <m@m544.net>
Co-authored-by: daima3629 <52790780+daima3629@users.noreply.github.com>
Co-authored-by: Windymelt <1113940+windymelt@users.noreply.github.com>
2023-02-26 20:21:54 +09:00
syuilo
1ba49b614d Merge pull request #10058 from misskey-dev/develop
Release: 13.7.5
2023-02-24 13:06:55 +09:00
tamaina
40de14415c Release: 13.7.4
Merge pull request #10050 from misskey-dev/develop
2023-02-23 23:11:25 +09:00
tamaina
7c9330a02f Release: 13.7.3
Merge pull request #10048 from misskey-dev/develop
2023-02-23 22:15:56 +09:00
309 changed files with 4250 additions and 2524 deletions

View File

@@ -5,7 +5,7 @@
"workspaceFolder": "/workspace", "workspaceFolder": "/workspace",
"features": { "features": {
"ghcr.io/devcontainers-contrib/features/pnpm:2": { "ghcr.io/devcontainers-contrib/features/pnpm:2": {
"version": "8.8.0" "version": "8.9.2"
}, },
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": "20.5.1" "version": "20.5.1"

View File

@@ -9,7 +9,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.0 uses: actions/checkout@v4.1.1
- run: corepack enable - run: corepack enable

View File

@@ -10,7 +10,7 @@ jobs:
check_copyright_year: check_copyright_year:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
- run: | - run: |
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
echo "Please change copyright year!" echo "Please change copyright year!"

View File

@@ -13,7 +13,7 @@ jobs:
if: github.repository == 'misskey-dev/misskey' if: github.repository == 'misskey-dev/misskey'
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v4.1.0 uses: actions/checkout@v4.1.1
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0

View File

@@ -12,7 +12,7 @@ jobs:
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v4.1.0 uses: actions/checkout@v4.1.1
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0

View File

@@ -14,7 +14,7 @@ jobs:
env: env:
DOCKER_CONTENT_TRUST: 1 DOCKER_CONTENT_TRUST: 1
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
- run: | - run: |
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb" curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
sudo dpkg -i dockle.deb sudo dpkg -i dockle.deb

225
.github/workflows/get-api-diff.yml vendored Normal file
View File

@@ -0,0 +1,225 @@
name: Report API Diff
on:
pull_request:
branches:
- master
- develop
jobs:
get-base:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
node-version: [20.5.1]
services:
db:
image: postgres:13
ports:
- 5432:5432
env:
POSTGRES_DB: misskey
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_USER: example-misskey-user
POSTGRESS_PASS: example-misskey-pass
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4.1.1
with:
repository: ${{ github.event.pull_request.base.repo.full_name }}
ref: ${{ github.base_ref }}
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.8.1
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .config/example.yml .config/default.yml
- name: Build
run: pnpm build
- name : Migrate
run: pnpm migrate
- name: Launch misskey
run: |
screen -S misskey -dm pnpm run dev
sleep 30s
- name: Wait for Misskey to be ready
run: |
MAX_RETRIES=12
RETRY_DELAY=5
count=0
until $(curl --output /dev/null --silent --head --fail http://localhost:3000) || [[ $count -eq $MAX_RETRIES ]]; do
printf '.'
sleep $RETRY_DELAY
count=$((count + 1))
done
if [[ $count -eq $MAX_RETRIES ]]; then
echo "Failed to connect to Misskey after $MAX_RETRIES attempts."
exit 1
fi
- id: fetch
name: Get api.json from Misskey
run: |
RESULT=$(curl --retry 5 --retry-delay 5 --retry-max-time 60 http://localhost:3000/api.json)
echo $RESULT > api-base.json
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: api-artifact
path: api-base.json
- name: Kill Misskey Job
run: screen -S misskey -X quit
get-head:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
node-version: [20.5.1]
services:
db:
image: postgres:13
ports:
- 5432:5432
env:
POSTGRES_DB: misskey
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_USER: example-misskey-user
POSTGRESS_PASS: example-misskey-pass
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4.1.1
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.8.1
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .config/example.yml .config/default.yml
- name: Build
run: pnpm build
- name : Migrate
run: pnpm migrate
- name: Launch misskey
run: |
screen -S misskey -dm pnpm run dev
sleep 30s
- name: Wait for Misskey to be ready
run: |
MAX_RETRIES=12
RETRY_DELAY=5
count=0
until $(curl --output /dev/null --silent --head --fail http://localhost:3000) || [[ $count -eq $MAX_RETRIES ]]; do
printf '.'
sleep $RETRY_DELAY
count=$((count + 1))
done
if [[ $count -eq $MAX_RETRIES ]]; then
echo "Failed to connect to Misskey after $MAX_RETRIES attempts."
exit 1
fi
- id: fetch
name: Get api.json from Misskey
run: |
RESULT=$(curl --retry 5 --retry-delay 5 --retry-max-time 60 http://localhost:3000/api.json)
echo $RESULT > api-head.json
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: api-artifact
path: api-head.json
- name: Kill Misskey Job
run: screen -S misskey -X quit
compare-diff:
runs-on: ubuntu-latest
if: success()
needs: [get-base, get-head]
permissions:
pull-requests: write
steps:
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: api-artifact
path: ./artifacts
- name: Output base
run: cat ./artifacts/api-base.json
- name: Output head
run: cat ./artifacts/api-head.json
- name: Arrange json files
run: |
jq '.' ./artifacts/api-base.json > ./api-base.json
jq '.' ./artifacts/api-head.json > ./api-head.json
- name: Get diff of 2 files
run: diff -u --label=base --label=head ./api-base.json ./api-head.json | cat > api.json.diff
- name: Get full diff
run: diff --label=base --label=head --new-line-format='+%L' --old-line-format='-%L' --unchanged-line-format=' %L' ./api-base.json ./api-head.json | cat > api-full.json.diff
- name: Echo full diff
run: cat ./api-full.json.diff
- name: Upload full diff to Artifact
uses: actions/upload-artifact@v3
with:
name: api-artifact
path: api-full.json.diff
- id: out-diff
name: Build diff Comment
run: |
cat <<- EOF > ./output.md
このPRによるapi.jsonの差分
<details>
<summary>差分はこちら</summary>
\`\`\`diff
$(cat ./api.json.diff)
\`\`\`
</details>
[Get diff files from Workflow Page](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})
EOF
- name: Write diff comment
uses: thollander/actions-comment-pull-request@v2
with:
comment_tag: show_diff
filePath: ./output.md

View File

@@ -11,7 +11,7 @@ jobs:
pnpm_install: pnpm_install:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
@@ -38,7 +38,7 @@ jobs:
- sw - sw
- misskey-js - misskey-js
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
@@ -64,7 +64,7 @@ jobs:
- backend - backend
- misskey-js - misskey-js
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true

View File

@@ -53,7 +53,7 @@ jobs:
# Check out merge commit # Check out merge commit
- name: Fork based /deploy checkout - name: Fork based /deploy checkout
uses: actions/checkout@v4.1.0 uses: actions/checkout@v4.1.1
with: with:
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge' ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'

View File

@@ -29,7 +29,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
submodules: true submodules: true
- name: Install pnpm - name: Install pnpm

View File

@@ -16,7 +16,7 @@ jobs:
node-version: [20.5.1] node-version: [20.5.1]
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
submodules: true submodules: true
- name: Install pnpm - name: Install pnpm
@@ -68,7 +68,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
submodules: true submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150 # https://github.com/cypress-io/cypress-docker-images/issues/150

View File

@@ -21,7 +21,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.0 uses: actions/checkout@v4.1.1
- run: corepack enable - run: corepack enable

View File

@@ -19,7 +19,7 @@ jobs:
node-version: [20.5.1] node-version: [20.5.1]
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v4.1.1
with: with:
submodules: true submodules: true
- name: Install pnpm - name: Install pnpm

View File

@@ -12,11 +12,47 @@
--> -->
## 2023.x.x (unreleased)
### General
- Feat: アンテナでローカルの投稿のみ収集できるようになりました
- Feat: サーバーサイレンス機能が追加されました
- Enhance: 新規にフォローした人の返信をデフォルトでTLに追加できるオプションを追加
- Enhance: HTL/LTL/STLを2023.10.0アップデート以前まで遡れるように
- Enhance: フォロー/フォロー解除したときに過去分のHTLにも含まれる投稿が反映されるように
- Enhance: ローカリゼーションの更新
- Enhance: 依存関係の更新
### Client
- Enhance: TLの返信表示オプションを記憶するように
- Enhance: 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく
### Server
- Enhance: タイムライン取得時のパフォーマンスを向上
- Enhance: ストリーミングAPIのパフォーマンスを向上
- Fix: users/notesでDBから参照した際にチャンネル投稿のみ取得される問題を修正
- Fix: コントロールパネルの設定項目が正しく保存できない問題を修正
- Fix: 管理者権限のロールを持っていても一部のAPIが使用できないことがある問題を修正
- Change: ユーザーのisCatがtrueでも、サーバーではnyaizeが行われなくなりました
- isCatな場合、クライアントでnyaize処理を行うことを推奨します
## 2023.10.1
### General
- Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に
### Client
- Fix: 絵文字ピッカーで横に長いカスタム絵文字が見切れる問題を修正
### Server
- Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正
- Fix: users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正
## 2023.10.0 ## 2023.10.0
### NOTE ### NOTE
- 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました - 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました
- アップデート後、アップデートより前の時点にTLを遡ることはできません - アップデートを行うと、タイムラインが一時的にリセットされます
-ップデート後であっても、今後のアップデートで2023.10.0以前のTLに遡れるようになる可能性はあります -ンテナ内のノートも含む
- ソフトミュート設定はクライアントではなくサーバー側に保存されるようになったため、アップデートを行うとソフトミュートの設定がリセットされます
### Changes ### Changes
- API: users/notes, notes/local-timeline で fileType 指定はできなくなりました - API: users/notes, notes/local-timeline で fileType 指定はできなくなりました
@@ -39,11 +75,15 @@
- Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正 - Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正
### Client ### Client
- Feat: 「ファイルの詳細」ページを追加
- ドライブのファイルの拡大プレビューができるように
- ファイルが添付されたノートの一覧が表示できるように
- Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に - Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に
- Enhance: 動画再生時のデフォルトボリュームを30%に - Enhance: 動画再生時のデフォルトボリュームを30%に
- Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正 - Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正
### Server ### Server
- Enhance: drive/files/attached-notes がページネーションに対応しました
- Enhance: タイムライン取得時のパフォーマンスを大幅に向上 - Enhance: タイムライン取得時のパフォーマンスを大幅に向上
- Enhance: ハイライト取得時のパフォーマンスを大幅に向上 - Enhance: ハイライト取得時のパフォーマンスを大幅に向上
- Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上 - Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上

View File

@@ -1,3 +1,4 @@
/* flaky
describe('After user signed in', () => { describe('After user signed in', () => {
beforeEach(() => { beforeEach(() => {
cy.resetState(); cy.resetState();
@@ -67,3 +68,4 @@ describe('After user signed in', () => {
buildWidgetTest('aiscript'); buildWidgetTest('aiscript');
buildWidgetTest('aichan'); buildWidgetTest('aichan');
}); });
*/

View File

@@ -995,9 +995,6 @@ _theme:
infoFg: "তথ্যের পাঠ্য" infoFg: "তথ্যের পাঠ্য"
infoWarnBg: "ওয়ার্নিং এর পটভূমি" infoWarnBg: "ওয়ার্নিং এর পটভূমি"
infoWarnFg: "ওয়ার্নিং এর পাঠ্য" infoWarnFg: "ওয়ার্নিং এর পাঠ্য"
cwBg: "CW বাটনের পটভূমি"
cwFg: "CW বাটনের পাঠ্য"
cwHoverBg: "CW বাটনের পটভূমি (হভার)"
toastBg: "বিজ্ঞপ্তির পটভূমি" toastBg: "বিজ্ঞপ্তির পটভূমি"
toastFg: "বিজ্ঞপ্তির পাঠ্য" toastFg: "বিজ্ঞপ্তির পাঠ্য"
buttonBg: "বাটনের পটভূমি" buttonBg: "বাটনের পটভূমি"

View File

@@ -1622,9 +1622,6 @@ _theme:
infoFg: "Text informací" infoFg: "Text informací"
infoWarnBg: "Pozadí varování" infoWarnBg: "Pozadí varování"
infoWarnFg: "Text varování" infoWarnFg: "Text varování"
cwBg: "Pozadí CW tlačítka"
cwFg: "Text CW tlačítka"
cwHoverBg: "Pozadí CW tlačítka (Hover)"
toastBg: "Pozadí oznámení" toastBg: "Pozadí oznámení"
toastFg: "Text oznámení" toastFg: "Text oznámení"
buttonBg: "Pozadí tlačítka" buttonBg: "Pozadí tlačítka"

View File

@@ -195,6 +195,7 @@ perHour: "Pro Stunde"
perDay: "Pro Tag" perDay: "Pro Tag"
stopActivityDelivery: "Senden von Aktivitäten einstellen" stopActivityDelivery: "Senden von Aktivitäten einstellen"
blockThisInstance: "Diese Instanz blockieren" blockThisInstance: "Diese Instanz blockieren"
silenceThisInstance: "Instanz stummschalten"
operations: "Aktionen" operations: "Aktionen"
software: "Software" software: "Software"
version: "Version" version: "Version"
@@ -214,6 +215,8 @@ clearCachedFiles: "Cache leeren"
clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen Instanzen wirklich gelöscht werden?" clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen Instanzen wirklich gelöscht werden?"
blockedInstances: "Blockierte Instanzen" blockedInstances: "Blockierte Instanzen"
blockedInstancesDescription: "Gib die Hostnamen der Instanzen, welche blockiert werden sollen, durch Zeilenumbrüche getrennt an. Blockierte Instanzen können mit dieser instanz nicht mehr kommunizieren." blockedInstancesDescription: "Gib die Hostnamen der Instanzen, welche blockiert werden sollen, durch Zeilenumbrüche getrennt an. Blockierte Instanzen können mit dieser instanz nicht mehr kommunizieren."
silencedInstances: "Stummgeschaltete Instanzen"
silencedInstancesDescription: "Gib die Hostnamen der Instanzen, welche stummgeschaltet werden sollen, durch Zeilenumbrüche getrennt an. Alle Konten dieser Instanzen werden als stummgeschaltet behandelt, können nur noch Follow-Anfragen stellen und wenn nicht gefolgt keine lokalen Konten erwähnen. Blockierte Instanzen sind davon nicht betroffen."
muteAndBlock: "Stummschaltungen und Blockierungen" muteAndBlock: "Stummschaltungen und Blockierungen"
mutedUsers: "Stummgeschaltete Benutzer" mutedUsers: "Stummgeschaltete Benutzer"
blockedUsers: "Blockierte Benutzer" blockedUsers: "Blockierte Benutzer"
@@ -531,6 +534,7 @@ serverLogs: "Serverprotokolle"
deleteAll: "Alle löschen" deleteAll: "Alle löschen"
showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen" showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen"
showFixedPostFormInChannel: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen (Kanäle)" showFixedPostFormInChannel: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen (Kanäle)"
withRepliesByDefaultForNewlyFollowed: "Standardmäßig Antworten von neu gefolgten Benutzern in der Chronik anzeigen"
newNoteRecived: "Es gibt neue Notizen" newNoteRecived: "Es gibt neue Notizen"
sounds: "Töne" sounds: "Töne"
sound: "Töne" sound: "Töne"
@@ -794,7 +798,7 @@ active: "Aktiv"
offline: "Offline" offline: "Offline"
notRecommended: "Nicht empfohlen" notRecommended: "Nicht empfohlen"
botProtection: "Schutz vor Bots" botProtection: "Schutz vor Bots"
instanceBlocking: "Blockierte Instanzen" instanceBlocking: "Blockierte/Stummgeschaltete Instanzen"
selectAccount: "Benutzerkonto auswählen" selectAccount: "Benutzerkonto auswählen"
switchAccount: "Konto wechseln" switchAccount: "Konto wechseln"
enabled: "Aktiviert" enabled: "Aktiviert"
@@ -1685,9 +1689,6 @@ _theme:
infoFg: "Text von Informationen" infoFg: "Text von Informationen"
infoWarnBg: "Hintergrund von Warnungen" infoWarnBg: "Hintergrund von Warnungen"
infoWarnFg: "Text von Warnungen" infoWarnFg: "Text von Warnungen"
cwBg: "Hintergrund des Inhaltswarnungsknopfs"
cwFg: "Text des Inhaltswarnungsknopfs"
cwHoverBg: "Hintergrund des Inhaltswarnungsknopfs (Mouseover)"
toastBg: "Hintergrund von Benachrichtigungen" toastBg: "Hintergrund von Benachrichtigungen"
toastFg: "Text von Benachrichtigungen" toastFg: "Text von Benachrichtigungen"
buttonBg: "Hintergrund von Schaltflächen" buttonBg: "Hintergrund von Schaltflächen"
@@ -1924,6 +1925,7 @@ _exportOrImport:
userLists: "Listen" userLists: "Listen"
excludeMutingUsers: "Stummgeschaltete Benutzer aussortieren" excludeMutingUsers: "Stummgeschaltete Benutzer aussortieren"
excludeInactiveUsers: "Inaktive Benutzer aussortieren" excludeInactiveUsers: "Inaktive Benutzer aussortieren"
withReplies: "Antworten von importierten Benutzern in der Chronik beinhalten"
_charts: _charts:
federation: "Föderation" federation: "Föderation"
apRequest: "Anfragen" apRequest: "Anfragen"
@@ -2144,3 +2146,11 @@ _moderationLogTypes:
createAd: "Werbung erstellt" createAd: "Werbung erstellt"
deleteAd: "Werbung gelöscht" deleteAd: "Werbung gelöscht"
updateAd: "Werbung aktualisiert" updateAd: "Werbung aktualisiert"
_fileViewer:
title: "Dateiinformationen"
type: "Dateityp"
size: "Dateigröße"
url: "URL"
uploadedAt: "Hochgeladen am"
attachedNotes: "Zugehörige Notizen"
thisPageCanBeSeenFromTheAuthor: "Nur der Benutzer, der diese Datei hochgeladen hat, kann diese Seite sehen."

View File

@@ -195,6 +195,7 @@ perHour: "Per Hour"
perDay: "Per Day" perDay: "Per Day"
stopActivityDelivery: "Stop sending activities" stopActivityDelivery: "Stop sending activities"
blockThisInstance: "Block this instance" blockThisInstance: "Block this instance"
silenceThisInstance: "Silence this instance"
operations: "Operations" operations: "Operations"
software: "Software" software: "Software"
version: "Version" version: "Version"
@@ -213,7 +214,9 @@ clearQueueConfirmText: "Any undelivered notes remaining in the queue will not be
clearCachedFiles: "Clear cache" clearCachedFiles: "Clear cache"
clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?" clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?"
blockedInstances: "Blocked Instances" blockedInstances: "Blocked Instances"
blockedInstancesDescription: "List the hostnames of the instances that you want to block separated by linebreaks. Listed instances will no longer be able to communicate with this instance." blockedInstancesDescription: "List the hostnames of the instances you want to block separated by linebreaks. Listed instances will no longer be able to communicate with this instance."
silencedInstances: "Silenced instances"
silencedInstancesDescription: "List the hostnames of the instances that you want to silence. All accounts of the listed instances will be treated as silenced, can only make follow requests, and cannot mention local accounts if not followed. This will not affect blocked instances."
muteAndBlock: "Mutes and Blocks" muteAndBlock: "Mutes and Blocks"
mutedUsers: "Muted users" mutedUsers: "Muted users"
blockedUsers: "Blocked users" blockedUsers: "Blocked users"
@@ -531,6 +534,7 @@ serverLogs: "Server logs"
deleteAll: "Delete all" deleteAll: "Delete all"
showFixedPostForm: "Display the posting form at the top of the timeline" showFixedPostForm: "Display the posting form at the top of the timeline"
showFixedPostFormInChannel: "Display the posting form at the top of the timeline (Channels)" showFixedPostFormInChannel: "Display the posting form at the top of the timeline (Channels)"
withRepliesByDefaultForNewlyFollowed: "Include replies by newly followed users in the timeline by default"
newNoteRecived: "There are new notes" newNoteRecived: "There are new notes"
sounds: "Sounds" sounds: "Sounds"
sound: "Sounds" sound: "Sounds"
@@ -589,7 +593,7 @@ poll: "Poll"
useCw: "Hide content" useCw: "Hide content"
enablePlayer: "Open video player" enablePlayer: "Open video player"
disablePlayer: "Close video player" disablePlayer: "Close video player"
expandTweet: "Expand tweet" expandTweet: "Expand post"
themeEditor: "Theme editor" themeEditor: "Theme editor"
description: "Description" description: "Description"
describeFile: "Add caption" describeFile: "Add caption"
@@ -794,7 +798,7 @@ active: "Active"
offline: "Offline" offline: "Offline"
notRecommended: "Not recommended" notRecommended: "Not recommended"
botProtection: "Bot Protection" botProtection: "Bot Protection"
instanceBlocking: "Blocked Instances" instanceBlocking: "Blocked/Silenced Instances"
selectAccount: "Select account" selectAccount: "Select account"
switchAccount: "Switch account" switchAccount: "Switch account"
enabled: "Enabled" enabled: "Enabled"
@@ -1126,8 +1130,8 @@ edited: "Edited"
notificationRecieveConfig: "Notification Settings" notificationRecieveConfig: "Notification Settings"
mutualFollow: "Mutual follow" mutualFollow: "Mutual follow"
fileAttachedOnly: "Only notes with files" fileAttachedOnly: "Only notes with files"
showRepliesToOthersInTimeline: "Show replies to others in TL" showRepliesToOthersInTimeline: "Show replies to others in timeline"
hideRepliesToOthersInTimeline: "Hide replies to others from TL" hideRepliesToOthersInTimeline: "Hide replies to others from timeline"
externalServices: "External Services" externalServices: "External Services"
impressum: "Impressum" impressum: "Impressum"
impressumUrl: "Impressum URL" impressumUrl: "Impressum URL"
@@ -1685,9 +1689,6 @@ _theme:
infoFg: "Information text" infoFg: "Information text"
infoWarnBg: "Warning background" infoWarnBg: "Warning background"
infoWarnFg: "Warning text" infoWarnFg: "Warning text"
cwBg: "CW button background"
cwFg: "CW button text"
cwHoverBg: "CW button background (Hover)"
toastBg: "Notification background" toastBg: "Notification background"
toastFg: "Notification text" toastFg: "Notification text"
buttonBg: "Button background" buttonBg: "Button background"
@@ -1741,7 +1742,7 @@ _2fa:
step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app." step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app."
step2Uri: "Enter the following URI if you are using a desktop program" step2Uri: "Enter the following URI if you are using a desktop program"
step3Title: "Enter an authentication code" step3Title: "Enter an authentication code"
step3: "Enter the token provided by your app to finish setup." step3: "Enter the authentication code (token) provided by your app to finish setup."
setupCompleted: "Setup complete" setupCompleted: "Setup complete"
step4: "From now on, any future login attempts will ask for such a login token." step4: "From now on, any future login attempts will ask for such a login token."
securityKeyNotSupported: "Your browser does not support security keys." securityKeyNotSupported: "Your browser does not support security keys."
@@ -1924,6 +1925,7 @@ _exportOrImport:
userLists: "User lists" userLists: "User lists"
excludeMutingUsers: "Exclude muted users" excludeMutingUsers: "Exclude muted users"
excludeInactiveUsers: "Exclude inactive users" excludeInactiveUsers: "Exclude inactive users"
withReplies: "Include replies from imported users in the timeline"
_charts: _charts:
federation: "Federation" federation: "Federation"
apRequest: "Requests" apRequest: "Requests"
@@ -2076,7 +2078,7 @@ _deck:
introduction: "Create the perfect interface for you by arranging columns freely!" introduction: "Create the perfect interface for you by arranging columns freely!"
introduction2: "Click on the + on the right of the screen to add new colums whenever you want." introduction2: "Click on the + on the right of the screen to add new colums whenever you want."
widgetsIntroduction: "Please select \"Edit widgets\" in the column menu and add a widget." widgetsIntroduction: "Please select \"Edit widgets\" in the column menu and add a widget."
useSimpleUiForNonRootPages: "Use simplified UI to navigated pages" useSimpleUiForNonRootPages: "Use simple UI for navigated pages"
usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled" usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled"
flexible: "Auto-adjust width" flexible: "Auto-adjust width"
_columns: _columns:
@@ -2144,3 +2146,11 @@ _moderationLogTypes:
createAd: "Ad created" createAd: "Ad created"
deleteAd: "Ad deleted" deleteAd: "Ad deleted"
updateAd: "Ad updated" updateAd: "Ad updated"
_fileViewer:
title: "File details"
type: "File type"
size: "Filesize"
url: "URL"
uploadedAt: "Uploaded at"
attachedNotes: "Attached notes"
thisPageCanBeSeenFromTheAuthor: "This page can only be seen by the user who uploaded this file."

View File

@@ -195,6 +195,7 @@ perHour: "por hora"
perDay: "por día" perDay: "por día"
stopActivityDelivery: "Dejar de enviar actividades" stopActivityDelivery: "Dejar de enviar actividades"
blockThisInstance: "Bloquear instancia" blockThisInstance: "Bloquear instancia"
silenceThisInstance: "Silenciar esta instancia"
operations: "Operaciones" operations: "Operaciones"
software: "Software" software: "Software"
version: "Versión" version: "Versión"
@@ -214,6 +215,8 @@ clearCachedFiles: "Limpiar caché"
clearCachedFilesConfirm: "¿Desea borrar todos los archivos remotos cacheados?" clearCachedFilesConfirm: "¿Desea borrar todos los archivos remotos cacheados?"
blockedInstances: "Instancias bloqueadas" blockedInstances: "Instancias bloqueadas"
blockedInstancesDescription: "Seleccione los hosts de las instancias que desea bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia." blockedInstancesDescription: "Seleccione los hosts de las instancias que desea bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia."
silencedInstances: "Instancias silenciadas"
silencedInstancesDescription: "Listar los hostname de las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas."
muteAndBlock: "Silenciar y bloquear" muteAndBlock: "Silenciar y bloquear"
mutedUsers: "Usuarios silenciados" mutedUsers: "Usuarios silenciados"
blockedUsers: "Usuarios bloqueados" blockedUsers: "Usuarios bloqueados"
@@ -531,6 +534,7 @@ serverLogs: "Registros del servidor"
deleteAll: "Eliminar todos" deleteAll: "Eliminar todos"
showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo" showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo"
showFixedPostFormInChannel: "Mostrar el formulario de publicación por encima de la cronología (Canales)" showFixedPostFormInChannel: "Mostrar el formulario de publicación por encima de la cronología (Canales)"
withRepliesByDefaultForNewlyFollowed: "Incluir por defecto respuestas de usuarios recién seguidos en la línea de tiempo"
newNoteRecived: "Tienes una nota nueva" newNoteRecived: "Tienes una nota nueva"
sounds: "Sonidos" sounds: "Sonidos"
sound: "Sonidos" sound: "Sonidos"
@@ -1121,6 +1125,20 @@ unnotifyNotes: "Dejar de notificar nuevas notas"
authentication: "Autenticación" authentication: "Autenticación"
authenticationRequiredToContinue: "Por favor, autentifícate para continuar" authenticationRequiredToContinue: "Por favor, autentifícate para continuar"
dateAndTime: "Fecha y hora" dateAndTime: "Fecha y hora"
showRenotes: "Mostrar renotas"
edited: "Editado"
notificationRecieveConfig: "Ajustes de Notificaciones"
mutualFollow: "Os seguís mutuamente"
fileAttachedOnly: "Solo notas con archivos"
showRepliesToOthersInTimeline: "Mostrar respuestas a otros en la línea de tiempo"
hideRepliesToOthersInTimeline: "Ocultar respuestas a otros en la línea de tiempo"
externalServices: "Servicios Externos"
impressum: "Impressum"
impressumUrl: "Impressum URL"
impressumDescription: "En algunos países, como Alemania, la inclusión del operador de datos (el Impressum) es requerido legalmente para sitios web comerciales."
privacyPolicy: "Política de Privacidad"
privacyPolicyUrl: "URL de la Política de Privacidad"
tosAndPrivacyPolicy: "Condiciones de Uso y Política de Privacidad"
_announcement: _announcement:
forExistingUsers: "Solo para usuarios registrados" forExistingUsers: "Solo para usuarios registrados"
forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán." forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán."
@@ -1470,6 +1488,7 @@ _role:
descriptionOfRateLimitFactor: "Límites más bajos son menos restrictivos, más altos menos restrictivos" descriptionOfRateLimitFactor: "Límites más bajos son menos restrictivos, más altos menos restrictivos"
canHideAds: "Puede ocultar anuncios" canHideAds: "Puede ocultar anuncios"
canSearchNotes: "Uso de la búsqueda de notas" canSearchNotes: "Uso de la búsqueda de notas"
canUseTranslator: "Uso de traductor"
_condition: _condition:
isLocal: "Usuario local" isLocal: "Usuario local"
isRemote: "Usuario remoto" isRemote: "Usuario remoto"
@@ -1518,6 +1537,10 @@ _ad:
reduceFrequencyOfThisAd: "Mostrar menos este anuncio." reduceFrequencyOfThisAd: "Mostrar menos este anuncio."
hide: "No mostrar" hide: "No mostrar"
timezoneinfo: "El día de la semana está determidado por la zona horaria del servidor." timezoneinfo: "El día de la semana está determidado por la zona horaria del servidor."
adsSettings: "Ajustes de anuncios"
notesPerOneAd: "Intervalo de actualización de anuncios en tiempo real (Notas por cada anuncio)"
setZeroToDisable: "Establece este valor a 0 para deshabilitar la actualización de anuncios en tiempo real"
adsTooClose: "El intervalo de anuncios actual puede empeorar la experiencia del usuario por ser demasiado bajo."
_forgotPassword: _forgotPassword:
enterEmail: "Ingrese el correo usado para registrar la cuenta. Se enviará un link para resetear la contraseña." enterEmail: "Ingrese el correo usado para registrar la cuenta. Se enviará un link para resetear la contraseña."
ifNoEmail: "Si no utilizó un correo para crear la cuenta, contáctese con el administrador." ifNoEmail: "Si no utilizó un correo para crear la cuenta, contáctese con el administrador."
@@ -1666,9 +1689,6 @@ _theme:
infoFg: "Texto de información" infoFg: "Texto de información"
infoWarnBg: "Fondo de advertencias" infoWarnBg: "Fondo de advertencias"
infoWarnFg: "Texto de advertencias" infoWarnFg: "Texto de advertencias"
cwBg: "Fondo del botón CW"
cwFg: "Texto del botón CW"
cwHoverBg: "Fondo del botón CW (hover)"
toastBg: "Fondo de notificaciones" toastBg: "Fondo de notificaciones"
toastFg: "Texto de notificaciones" toastFg: "Texto de notificaciones"
buttonBg: "Fondo de botón" buttonBg: "Fondo de botón"
@@ -1905,6 +1925,7 @@ _exportOrImport:
userLists: "Listas" userLists: "Listas"
excludeMutingUsers: "Excluir usuarios silenciados" excludeMutingUsers: "Excluir usuarios silenciados"
excludeInactiveUsers: "Excluir usuarios inactivos" excludeInactiveUsers: "Excluir usuarios inactivos"
withReplies: "Incluir respuestas de los usuarios importados en la línea de tiempo"
_charts: _charts:
federation: "Federación" federation: "Federación"
apRequest: "Pedidos" apRequest: "Pedidos"
@@ -2122,3 +2143,14 @@ _moderationLogTypes:
unmarkSensitiveDriveFile: "Archivo marcado como no sensible" unmarkSensitiveDriveFile: "Archivo marcado como no sensible"
resolveAbuseReport: "Reporte resuelto" resolveAbuseReport: "Reporte resuelto"
createInvitation: "Generar invitación" createInvitation: "Generar invitación"
createAd: "Anuncio creado"
deleteAd: "Anuncio eliminado"
updateAd: "Anuncio actualizado"
_fileViewer:
title: "Detalles del archivo"
type: "Tipo de archivo"
size: "Tamaño del archivo"
url: "URL"
uploadedAt: "Subido el"
attachedNotes: "Notas adjuntas"
thisPageCanBeSeenFromTheAuthor: "Esta página solo puede ser vista por el autor."

View File

@@ -45,6 +45,7 @@ pin: "Épingler sur le profil"
unpin: "Désépingler" unpin: "Désépingler"
copyContent: "Copier le contenu" copyContent: "Copier le contenu"
copyLink: "Copier le lien" copyLink: "Copier le lien"
copyLinkRenote: "Copier le lien de la renote"
delete: "Supprimer" delete: "Supprimer"
deleteAndEdit: "Supprimer et réécrire" deleteAndEdit: "Supprimer et réécrire"
deleteAndEditConfirm: "Êtes-vous sûr de vouloir effacer cette note et la modifier ? Vous perdrez toutes les réactions, renotes et réponses." deleteAndEditConfirm: "Êtes-vous sûr de vouloir effacer cette note et la modifier ? Vous perdrez toutes les réactions, renotes et réponses."
@@ -129,6 +130,8 @@ unmarkAsSensitive: "Supprimer le marquage comme sensible"
enterFileName: "Entrer le nom du fichier" enterFileName: "Entrer le nom du fichier"
mute: "Masquer" mute: "Masquer"
unmute: "Ne plus masquer" unmute: "Ne plus masquer"
renoteMute: "Masquer les renotes"
renoteUnmute: "Ne plus masquer les renotes"
block: "Bloquer" block: "Bloquer"
unblock: "Débloquer" unblock: "Débloquer"
suspend: "Suspendre" suspend: "Suspendre"
@@ -181,7 +184,7 @@ selectUser: "Sélectionner un·e utilisateur·rice"
recipient: "Destinataire" recipient: "Destinataire"
annotation: "Commentaires" annotation: "Commentaires"
federation: "Fédération" federation: "Fédération"
instances: "Instance" instances: "Instances"
registeredAt: "Premier contact le" registeredAt: "Premier contact le"
latestRequestReceivedAt: "Dernière requête reçue" latestRequestReceivedAt: "Dernière requête reçue"
latestStatus: "Dernier statut" latestStatus: "Dernier statut"
@@ -191,6 +194,7 @@ perHour: "par heure"
perDay: "par jour" perDay: "par jour"
stopActivityDelivery: "Arrêter lenvoi de lactivité" stopActivityDelivery: "Arrêter lenvoi de lactivité"
blockThisInstance: "Bloquer cette instance" blockThisInstance: "Bloquer cette instance"
silenceThisInstance: "Mettre cette instance en sourdine"
operations: "Opérations" operations: "Opérations"
software: "Logiciel" software: "Logiciel"
version: "Version" version: "Version"
@@ -210,6 +214,8 @@ clearCachedFiles: "Vider le cache"
clearCachedFilesConfirm: "Êtes-vous sûr·e de vouloir vider tout le cache de fichiers distants ?" clearCachedFilesConfirm: "Êtes-vous sûr·e de vouloir vider tout le cache de fichiers distants ?"
blockedInstances: "Instances bloquées" blockedInstances: "Instances bloquées"
blockedInstancesDescription: "Listez les instances que vous désirez bloquer, une par ligne. Ces instances ne seront plus en capacité d'interagir avec votre instance." blockedInstancesDescription: "Listez les instances que vous désirez bloquer, une par ligne. Ces instances ne seront plus en capacité d'interagir avec votre instance."
silencedInstances: "Instances mises en sourdine"
silencedInstancesDescription: "Énumérer les noms d'hôte des instances à mettre en sourdine. Tous les comptes des instances énumérées seront traités comme mis en sourdine, ne peuvent faire que des demandes de suivi et ne peuvent pas mentionner les comptes locaux s'ils ne sont pas suivis. Cela n'affectera pas les instances bloquées."
muteAndBlock: "Masqué·e·s / Bloqué·e·s" muteAndBlock: "Masqué·e·s / Bloqué·e·s"
mutedUsers: "Utilisateur·rice·s en sourdine" mutedUsers: "Utilisateur·rice·s en sourdine"
blockedUsers: "Utilisateur·rice·s bloqué·e·s" blockedUsers: "Utilisateur·rice·s bloqué·e·s"
@@ -381,7 +387,7 @@ antennaSource: "Source de lantenne"
antennaKeywords: "Mots clés à recevoir" antennaKeywords: "Mots clés à recevoir"
antennaExcludeKeywords: "Mots clés à exclure" antennaExcludeKeywords: "Mots clés à exclure"
antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR."
notifyAntenna: "Je souhaite recevoir les notifications des nouvelles notes" notifyAntenna: "Me notifier pour les nouvelles notes"
withFileAntenna: "Notes ayant des attachements uniquement" withFileAntenna: "Notes ayant des attachements uniquement"
enableServiceworker: "Activer ServiceWorker" enableServiceworker: "Activer ServiceWorker"
antennaUsersDescription: "Saisissez un seul nom dutilisateur·rice par ligne" antennaUsersDescription: "Saisissez un seul nom dutilisateur·rice par ligne"
@@ -414,6 +420,7 @@ moderator: "Modérateur·rice·s"
moderation: "Modérations" moderation: "Modérations"
moderationNote: "Note de modération" moderationNote: "Note de modération"
addModerationNote: "Ajouter une note de modération" addModerationNote: "Ajouter une note de modération"
moderationLogs: "Journal de modération"
nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s" nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s"
securityKeyAndPasskey: "Sécurité et clés de sécurité" securityKeyAndPasskey: "Sécurité et clés de sécurité"
securityKey: "Clé de sécurité" securityKey: "Clé de sécurité"
@@ -472,6 +479,7 @@ aboutX: "À propos de {x}"
emojiStyle: "Style des émojis" emojiStyle: "Style des émojis"
native: "Natif" native: "Natif"
disableDrawer: "Les menus ne s'affichent pas dans le tiroir" disableDrawer: "Les menus ne s'affichent pas dans le tiroir"
showNoteActionsOnlyHover: "Afficher les actions de note uniquement au survol"
noHistory: "Pas d'historique" noHistory: "Pas d'historique"
signinHistory: "Historique de connexion" signinHistory: "Historique de connexion"
enableAdvancedMfm: "Activer la MFM avancée" enableAdvancedMfm: "Activer la MFM avancée"
@@ -647,6 +655,7 @@ behavior: "Comportement"
sample: "Exemple" sample: "Exemple"
abuseReports: "Signalements" abuseReports: "Signalements"
reportAbuse: "Signaler" reportAbuse: "Signaler"
reportAbuseRenote: "Signaler la renote"
reportAbuseOf: "Signaler {name}" reportAbuseOf: "Signaler {name}"
fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien." fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien."
abuseReported: "Le rapport est envoyé. Merci." abuseReported: "Le rapport est envoyé. Merci."
@@ -671,6 +680,8 @@ clip: "Clip"
createNew: "Créer nouveau" createNew: "Créer nouveau"
optional: "Facultatif" optional: "Facultatif"
createNewClip: "Créer un nouveau clip" createNewClip: "Créer un nouveau clip"
unclip: "Supprimer le clip"
confirmToUnclipAlreadyClippedNote: "Cette note fait déjà partie du clip « {name} ». Souhaitez-vous la supprimer de ce clip ?"
public: "Public" public: "Public"
private: "Privé" private: "Privé"
i18nInfo: "Misskey est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}." i18nInfo: "Misskey est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}."
@@ -919,6 +930,7 @@ remoteOnly: "Distant uniquement"
failedToUpload: "Échec du transfert" failedToUpload: "Échec du transfert"
cannotUploadBecauseInappropriate: "Impossible de télécharger le document car il a été déterminé qu'il pouvait contenir un contenu inapproprié." cannotUploadBecauseInappropriate: "Impossible de télécharger le document car il a été déterminé qu'il pouvait contenir un contenu inapproprié."
cannotUploadBecauseNoFreeSpace: "Impossible de télécharger en raison d'un manque d'espace libre sur le disque.\n" cannotUploadBecauseNoFreeSpace: "Impossible de télécharger en raison d'un manque d'espace libre sur le disque.\n"
cannotUploadBecauseExceedsFileSizeLimit: "Ce fichier ne peut pas être téléchargé parce qu'il dépasse la taille maximale."
beta: "Bêta" beta: "Bêta"
enableAutoSensitive: "Détermination automatique de NSFW" enableAutoSensitive: "Détermination automatique de NSFW"
enableAutoSensitiveDescription: "S'il est disponible, le drapeau NSFW est automatiquement défini sur le média en utilisant l'apprentissage automatique. Même si cette fonction est désactivée, elle peut être réglée automatiquement dans certains cas." enableAutoSensitiveDescription: "S'il est disponible, le drapeau NSFW est automatiquement défini sur le média en utilisant l'apprentissage automatique. Même si cette fonction est désactivée, elle peut être réglée automatiquement dans certains cas."
@@ -933,37 +945,55 @@ unsubscribePushNotification: "Désactiver les notifications push"
pushNotificationAlreadySubscribed: "Les notifications push sont déjà activées" pushNotificationAlreadySubscribed: "Les notifications push sont déjà activées"
pushNotificationNotSupported: "Votre navigateur ou votre instance ne prend pas en charge les notifications push" pushNotificationNotSupported: "Votre navigateur ou votre instance ne prend pas en charge les notifications push"
sendPushNotificationReadMessage: "Supprimer les notifications push une fois que les notifications ou messages pertinents ont été lus." sendPushNotificationReadMessage: "Supprimer les notifications push une fois que les notifications ou messages pertinents ont été lus."
windowMaximize: "Maximiser"
windowMinimize: "Minimaliser"
windowRestore: "Restaurer" windowRestore: "Restaurer"
caption: "Libellé" caption: "Libellé"
loggedInAsBot: "Connecté actuellement en tant que bot" loggedInAsBot: "Connecté actuellement en tant que bot"
tools: "Outils" tools: "Outils"
cannotLoad: "Chargement impossible" cannotLoad: "Chargement impossible"
numberOfProfileView: "Nombre de vues du profil"
like: "J'aime" like: "J'aime"
unlike: "Ne plus aimer"
numberOfLikes: "Favoris" numberOfLikes: "Favoris"
show: "Affichage" show: "Affichage"
neverShow: "Ne plus afficher" neverShow: "Ne plus afficher"
remindMeLater: "Peut-être plus tard" remindMeLater: "Peut-être plus tard"
didYouLikeMisskey: "Avez-vous aimé Misskey ?"
roles: "Rôles" roles: "Rôles"
role: "Rôles" role: "Rôles"
noRole: "Aucun rôle" noRole: "Aucun rôle"
normalUser: "Simple utilisateur·rice" normalUser: "Simple utilisateur·rice"
undefined: "Non défini" undefined: "Non défini"
assign: "Attribuer" assign: "Attribuer"
unassign: "Retirer"
color: "Couleur" color: "Couleur"
manageCustomEmojis: "Gestion des émojis personnalisés" manageCustomEmojis: "Gestion des émojis personnalisés"
youCannotCreateAnymore: "Vous avez atteint la limite de création."
cannotPerformTemporary: "Temporairement indisponible"
invalidParamError: "Paramètres invalides"
preset: "Préréglage" preset: "Préréglage"
selectFromPresets: "Sélectionner à partir des préréglages" selectFromPresets: "Sélectionner à partir des préréglages"
achievements: "Accomplissements" achievements: "Accomplissements"
gotInvalidResponseError: "Réponse du serveur invalide"
thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes." thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes."
thisPostMayBeAnnoyingHome: "Publier vers le fil principal" thisPostMayBeAnnoyingHome: "Publier vers le fil principal"
thisPostMayBeAnnoyingCancel: "Annuler" thisPostMayBeAnnoyingCancel: "Annuler"
thisPostMayBeAnnoyingIgnore: "Publier quand-même" thisPostMayBeAnnoyingIgnore: "Publier quand-même"
collapseRenotes: "Réduire les renotes déjà vues"
internalServerError: "Erreur interne du serveur" internalServerError: "Erreur interne du serveur"
copyErrorInfo: "Copier les détails de lerreur" copyErrorInfo: "Copier les détails de lerreur"
exploreOtherServers: "Trouver une autre instance" exploreOtherServers: "Trouver une autre instance"
disableFederationOk: "Désactiver" disableFederationOk: "Désactiver"
postToTheChannel: "Publier au canal"
likeOnly: "Les favoris uniquement" likeOnly: "Les favoris uniquement"
sensitiveWords: "Mots sensibles"
notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
license: "Licence" license: "Licence"
myClips: "Mes clips"
retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
showClipButtonInNoteFooter: "Ajouter « Clip » au menu d'action de la note"
noteIdOrUrl: "Identifiant de la note ou URL"
video: "Vidéo" video: "Vidéo"
videos: "Vidéos" videos: "Vidéos"
dataSaver: "Économiseur de données" dataSaver: "Économiseur de données"
@@ -971,29 +1001,44 @@ accountMigration: "Migration de compte"
accountMoved: "Cet·te utilisateur·rice a migré son compte vers :" accountMoved: "Cet·te utilisateur·rice a migré son compte vers :"
accountMovedShort: "Ce compte a migré" accountMovedShort: "Ce compte a migré"
operationForbidden: "Opération non autorisée" operationForbidden: "Opération non autorisée"
forceShowAds: "Toujours afficher les publicités"
addMemo: "Ajouter un mémo" addMemo: "Ajouter un mémo"
reactionsList: "Réactions" reactionsList: "Réactions"
renotesList: "Liste de renotes"
notificationDisplay: "Style des notifications" notificationDisplay: "Style des notifications"
leftTop: "En haut à gauche" leftTop: "En haut à gauche"
rightTop: "En haut à droite" rightTop: "En haut à droite"
leftBottom: "En bas à gauche" leftBottom: "En bas à gauche"
rightBottom: "En bas à droite" rightBottom: "En bas à droite"
stackAxis: "Direction d'empilement"
vertical: "Vertical" vertical: "Vertical"
horizontal: "Latéral" horizontal: "Latéral"
position: "Position"
serverRules: "Règles du serveur" serverRules: "Règles du serveur"
pleaseAgreeAllToContinue: "Pour continuer, veuillez accepter tous les champs ci-dessus."
continue: "Continuer"
preservedUsernames: "Noms d'utilisateur·rice réservés"
archive: "Archive" archive: "Archive"
displayOfNote: "Affichage de la note"
initialAccountSetting: "Réglage initial du profil"
youFollowing: "Abonné·e" youFollowing: "Abonné·e"
preventAiLearning: "Refuser l'usage dans l'apprentissage automatique d'IA générative"
options: "Options" options: "Options"
later: "Plus tard" later: "Plus tard"
goToMisskey: "Retour vers Misskey" goToMisskey: "Retour vers Misskey"
expirationDate: "Date dexpiration" expirationDate: "Date dexpiration"
waitingForMailAuth: "En attente de la vérification de l'adresse courriel"
usedAt: "Utilisé le" usedAt: "Utilisé le"
unused: "Non-utilisé" unused: "Non-utilisé"
used: "Utilisé" used: "Utilisé"
expired: "Expiré" expired: "Expiré"
doYouAgree: "Êtes-vous daccord ?" doYouAgree: "Êtes-vous daccord ?"
beSureToReadThisAsItIsImportant: "Assurez-vous de le lire; c'est important."
dialog: "Dialogue"
icon: "Avatar" icon: "Avatar"
forYou: "Pour vous" forYou: "Pour vous"
currentAnnouncements: "Annonces actuelles"
pastAnnouncements: "Annonces passées"
replies: "Répondre" replies: "Répondre"
renotes: "Renoter" renotes: "Renoter"
loadReplies: "Inclure les réponses" loadReplies: "Inclure les réponses"
@@ -1001,6 +1046,7 @@ pinnedList: "Liste épinglée"
notifyNotes: "Notifier à propos des nouvelles notes" notifyNotes: "Notifier à propos des nouvelles notes"
authentication: "Authentification" authentication: "Authentification"
authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer" authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer"
showRenotes: "Afficher les renotes"
_announcement: _announcement:
readConfirmTitle: "Marquer comme lu ?" readConfirmTitle: "Marquer comme lu ?"
_initialAccountSetting: _initialAccountSetting:
@@ -1082,12 +1128,20 @@ _achievements:
title: "Beaucoup d'amis" title: "Beaucoup d'amis"
_followers10: _followers10:
title: "Abonnez-moi !" title: "Abonnez-moi !"
description: "Obtenir plus de 10 abonné·e·s"
_followers50:
description: "Obtenir plus de 50 abonné·e·s"
_followers100: _followers100:
title: "Populaire" title: "Populaire"
description: "Obtenir plus de 100 abonné·e·s"
_followers300:
description: "Obtenir plus de 300 abonné·e·s"
_followers500: _followers500:
title: "Tour radio" title: "Tour radio"
description: "Obtenir plus de 500 abonné·e·s"
_followers1000: _followers1000:
title: "Influenceur·euse" title: "Influenceur·euse"
description: "Obtenir plus de 1000 abonné·e·s"
_iLoveMisskey: _iLoveMisskey:
title: "Jadore Misskey" title: "Jadore Misskey"
description: "Publication « J❤ #Misskey »" description: "Publication « J❤ #Misskey »"
@@ -1151,6 +1205,7 @@ _role:
high: "Haute" high: "Haute"
_options: _options:
canManageCustomEmojis: "Gestion des émojis personnalisés" canManageCustomEmojis: "Gestion des émojis personnalisés"
wordMuteMax: "Nombre maximal de caractères dans le filtre de mots"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement." description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
sensitivity: "Sensibilité de la détection" sensitivity: "Sensibilité de la détection"
@@ -1330,9 +1385,6 @@ _theme:
infoFg: "Texte d'information" infoFg: "Texte d'information"
infoWarnBg: "Arrière-plan des avertissements" infoWarnBg: "Arrière-plan des avertissements"
infoWarnFg: "Texte davertissement" infoWarnFg: "Texte davertissement"
cwBg: "Arrière-plan du CW"
cwFg: "Texte du bouton CW"
cwHoverBg: "Arrière-plan du bouton CW (survolé)"
toastBg: "Arrière-plan de la bulle de notification" toastBg: "Arrière-plan de la bulle de notification"
toastFg: "Texte de la bulle de notification" toastFg: "Texte de la bulle de notification"
buttonBg: "Arrière-plan du bouton" buttonBg: "Arrière-plan du bouton"
@@ -1506,7 +1558,7 @@ _visibility:
_postForm: _postForm:
replyPlaceholder: "Répondre à cette note ..." replyPlaceholder: "Répondre à cette note ..."
quotePlaceholder: "Citez cette note ..." quotePlaceholder: "Citez cette note ..."
channelPlaceholder: "Publier vers le canal" channelPlaceholder: "Publier au canal"
_placeholders: _placeholders:
a: "Quoi de neuf ?" a: "Quoi de neuf ?"
b: "Il s'est passé quelque chose ?" b: "Il s'est passé quelque chose ?"

View File

@@ -1 +1,5 @@
--- ---
_lang_: "japanski"
ok: "OK"
gotIt: "Razumijem"
cancel: "otkazati"

View File

@@ -1 +1,18 @@
--- ---
_lang_: "Japonè"
password: "modpas"
ok: "OK"
gotIt: "Konprann"
cancel: "anile"
noThankYou: "Sispann"
instance: "sèvè"
profile: "pwofil"
save: "kenbe"
delete: "efase"
instances: "sèvè"
remove: "efase"
smtpPass: "modpas"
_2fa:
renewTOTPCancel: "Sispann"
_widgets:
profile: "pwofil"

View File

@@ -1627,9 +1627,6 @@ _theme:
infoFg: "Teks informasi" infoFg: "Teks informasi"
infoWarnBg: "Latar belakang peringatan" infoWarnBg: "Latar belakang peringatan"
infoWarnFg: "Teks peringatan" infoWarnFg: "Teks peringatan"
cwBg: "Latar belakang tombol Sembunyikan Konten"
cwFg: "Teks tombol Sembunyikan Konten"
cwHoverBg: "Latar belakang tombol Sembunyikan Konten (Mengambang)"
toastBg: "Latar belakang notifikasi" toastBg: "Latar belakang notifikasi"
toastFg: "Teks notifikasi" toastFg: "Teks notifikasi"
buttonBg: "Latar belakang tombol" buttonBg: "Latar belakang tombol"

17
locales/index.d.ts vendored
View File

@@ -198,6 +198,7 @@ export interface Locale {
"perDay": string; "perDay": string;
"stopActivityDelivery": string; "stopActivityDelivery": string;
"blockThisInstance": string; "blockThisInstance": string;
"silenceThisInstance": string;
"operations": string; "operations": string;
"software": string; "software": string;
"version": string; "version": string;
@@ -217,6 +218,8 @@ export interface Locale {
"clearCachedFilesConfirm": string; "clearCachedFilesConfirm": string;
"blockedInstances": string; "blockedInstances": string;
"blockedInstancesDescription": string; "blockedInstancesDescription": string;
"silencedInstances": string;
"silencedInstancesDescription": string;
"muteAndBlock": string; "muteAndBlock": string;
"mutedUsers": string; "mutedUsers": string;
"blockedUsers": string; "blockedUsers": string;
@@ -534,6 +537,7 @@ export interface Locale {
"deleteAll": string; "deleteAll": string;
"showFixedPostForm": string; "showFixedPostForm": string;
"showFixedPostFormInChannel": string; "showFixedPostFormInChannel": string;
"withRepliesByDefaultForNewlyFollowed": string;
"newNoteRecived": string; "newNoteRecived": string;
"sounds": string; "sounds": string;
"sound": string; "sound": string;
@@ -1796,9 +1800,6 @@ export interface Locale {
"infoFg": string; "infoFg": string;
"infoWarnBg": string; "infoWarnBg": string;
"infoWarnFg": string; "infoWarnFg": string;
"cwBg": string;
"cwFg": string;
"cwHoverBg": string;
"toastBg": string; "toastBg": string;
"toastFg": string; "toastFg": string;
"buttonBg": string; "buttonBg": string;
@@ -2054,6 +2055,7 @@ export interface Locale {
"userLists": string; "userLists": string;
"excludeMutingUsers": string; "excludeMutingUsers": string;
"excludeInactiveUsers": string; "excludeInactiveUsers": string;
"withReplies": string;
}; };
"_charts": { "_charts": {
"federation": string; "federation": string;
@@ -2294,6 +2296,15 @@ export interface Locale {
"deleteAd": string; "deleteAd": string;
"updateAd": string; "updateAd": string;
}; };
"_fileViewer": {
"title": string;
"type": string;
"size": string;
"url": string;
"uploadedAt": string;
"attachedNotes": string;
"thisPageCanBeSeenFromTheAuthor": string;
};
} }
declare const locales: { declare const locales: {
[lang: string]: Locale; [lang: string]: Locale;

View File

@@ -110,14 +110,14 @@ unrenote: "Elimina la Rinota"
renoted: "Rinotato!" renoted: "Rinotato!"
cantRenote: "È impossibile rinotare questa nota." cantRenote: "È impossibile rinotare questa nota."
cantReRenote: "È impossibile rinotare una Rinota." cantReRenote: "È impossibile rinotare una Rinota."
quote: "Cita" quote: "Citazione"
inChannelRenote: "Rinota nel canale" inChannelRenote: "Rinota nel canale"
inChannelQuote: "Cita nel canale" inChannelQuote: "Cita nel canale"
pinnedNote: "Nota fissata" pinnedNote: "Nota in primo piano"
pinned: "Fissa sul profilo" pinned: "Fissa sul profilo"
you: "Tu" you: "Tu"
clickToShow: "Clicca per visualizzare" clickToShow: "Contenuto occultato, cliccare solo se si intende vedere"
sensitive: "Esplicito" sensitive: "Allegato esplicito"
add: "Aggiungi" add: "Aggiungi"
reaction: "Reazioni" reaction: "Reazioni"
reactions: "Reazioni" reactions: "Reazioni"
@@ -195,6 +195,7 @@ perHour: "orario"
perDay: "giornaliero" perDay: "giornaliero"
stopActivityDelivery: "Interrompi la distribuzione di attività" stopActivityDelivery: "Interrompi la distribuzione di attività"
blockThisInstance: "Blocca questa istanza" blockThisInstance: "Blocca questa istanza"
silenceThisInstance: "Silenzia l'istanza"
operations: "Operazioni" operations: "Operazioni"
software: "Software" software: "Software"
version: "Versione" version: "Versione"
@@ -214,6 +215,8 @@ clearCachedFiles: "Svuota cache"
clearCachedFilesConfirm: "Vuoi davvero svuotare la cache da tutti i file remoti?" clearCachedFilesConfirm: "Vuoi davvero svuotare la cache da tutti i file remoti?"
blockedInstances: "Istanze bloccate" blockedInstances: "Istanze bloccate"
blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza." blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza."
silencedInstances: "Istanze silenziate"
silencedInstancesDescription: "Elenca i nomi host delle istanze che vuoi silenziare. Tutti i profili nelle istanze silenziate vengono trattati come tali. Possono solo inviare richieste di follow e menzionare soltanto i profili locali che seguono. Le istanze bloccate non sono interessate."
muteAndBlock: "Silenziati / Bloccati" muteAndBlock: "Silenziati / Bloccati"
mutedUsers: "Profili silenziati" mutedUsers: "Profili silenziati"
blockedUsers: "Profili bloccati" blockedUsers: "Profili bloccati"
@@ -278,7 +281,7 @@ agreeTo: "Sono d'accordo con {0}"
agree: "Accetto" agree: "Accetto"
agreeBelow: "Accetto quanto riportato sotto" agreeBelow: "Accetto quanto riportato sotto"
basicNotesBeforeCreateAccount: "Note importanti" basicNotesBeforeCreateAccount: "Note importanti"
termsOfService: "Informativa ai sensi degli artt. 13 e 14 del Regolamento UE 2016/679 per la protezione dei dati personali (GDPR)" termsOfService: "Condizioni d'uso del servizio"
start: "Inizia!" start: "Inizia!"
home: "Home" home: "Home"
remoteUserCaution: "Le informazioni potrebbero essere incomplete poiché questo profilo remoto potrebbe non essere completamente federato." remoteUserCaution: "Le informazioni potrebbero essere incomplete poiché questo profilo remoto potrebbe non essere completamente federato."
@@ -364,7 +367,7 @@ pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagi
pinnedPages: "Pagine in evidenza" pinnedPages: "Pagine in evidenza"
pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga." pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga."
pinnedClipId: "ID della Clip in evidenza" pinnedClipId: "ID della Clip in evidenza"
pinnedNotes: "Nota fissata" pinnedNotes: "Note in primo piano"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
enableHcaptcha: "Abilita hCaptcha" enableHcaptcha: "Abilita hCaptcha"
hcaptchaSiteKey: "Chiave del sito" hcaptchaSiteKey: "Chiave del sito"
@@ -384,7 +387,7 @@ name: "Nome"
antennaSource: "Fonte dell'antenna" antennaSource: "Fonte dell'antenna"
antennaKeywords: "Parole chiavi da ricevere" antennaKeywords: "Parole chiavi da ricevere"
antennaExcludeKeywords: "Parole chiavi da escludere" antennaExcludeKeywords: "Parole chiavi da escludere"
antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
notifyAntenna: "Invia notifiche delle nuove note" notifyAntenna: "Invia notifiche delle nuove note"
withFileAntenna: "Solo note con file in allegato" withFileAntenna: "Solo note con file in allegato"
enableServiceworker: "Abilita ServiceWorker" enableServiceworker: "Abilita ServiceWorker"
@@ -393,7 +396,7 @@ caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole"
withReplies: "Includere le risposte" withReplies: "Includere le risposte"
connectedTo: "Connessione ai seguenti profili:" connectedTo: "Connessione ai seguenti profili:"
notesAndReplies: "Note e risposte" notesAndReplies: "Note e risposte"
withFiles: "Con file in allegato" withFiles: "Con allegati"
silence: "Silenzia" silence: "Silenzia"
silenceConfirm: "Vuoi davvero silenziare questo profilo?" silenceConfirm: "Vuoi davvero silenziare questo profilo?"
unsilence: "Riattiva" unsilence: "Riattiva"
@@ -531,6 +534,7 @@ serverLogs: "Log del server"
deleteAll: "Cancella cronologia" deleteAll: "Cancella cronologia"
showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline" showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline"
showFixedPostFormInChannel: "Per i canali, mostra il modulo di pubblicazione in cima alla timeline" showFixedPostFormInChannel: "Per i canali, mostra il modulo di pubblicazione in cima alla timeline"
withRepliesByDefaultForNewlyFollowed: "Quando segui nuovi profili, includi le risposte in TL come impostazione predefinita"
newNoteRecived: "Nuove note da leggere" newNoteRecived: "Nuove note da leggere"
sounds: "Impostazioni suoni" sounds: "Impostazioni suoni"
sound: "Suono" sound: "Suono"
@@ -1121,20 +1125,20 @@ unnotifyNotes: "Interrompi le notifiche di nuove Note"
authentication: "Autenticazione" authentication: "Autenticazione"
authenticationRequiredToContinue: "Per procedere, è richiesta l'autenticazione" authenticationRequiredToContinue: "Per procedere, è richiesta l'autenticazione"
dateAndTime: "Data e Ora" dateAndTime: "Data e Ora"
showRenotes: "Leggi le Rinota" showRenotes: "Includi le Rinota"
edited: "Modificato" edited: "Modificato"
notificationRecieveConfig: "Preferenze di notifica" notificationRecieveConfig: "Preferenze di notifica"
mutualFollow: "Follow reciproco" mutualFollow: "Follow reciproco"
fileAttachedOnly: "Con file in allegato" fileAttachedOnly: "Solo con allegati"
showRepliesToOthersInTimeline: "Risposte altrui nella TL" showRepliesToOthersInTimeline: "Risposte altrui nella TL"
hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL" hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL"
externalServices: "Servizi esterni" externalServices: "Servizi esterni"
impressum: "Dichiarazione di proprietà" impressum: "Dichiarazione di proprietà"
impressumUrl: "URL della dichiarazione di proprietà" impressumUrl: "URL della dichiarazione di proprietà"
impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)." impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)."
privacyPolicy: "Informativa sulla privacy" privacyPolicy: "Informativa privacy ai sensi del Regolamento UE 2016/679 (GDPR)"
privacyPolicyUrl: "URL della informativa privacy" privacyPolicyUrl: "URL della informativa privacy"
tosAndPrivacyPolicy: "Condizioni d'uso e informativa sulla privacy" tosAndPrivacyPolicy: "Condizioni d'uso e informativa privacy"
_announcement: _announcement:
forExistingUsers: "Solo ai profili attuali" forExistingUsers: "Solo ai profili attuali"
forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
@@ -1533,6 +1537,10 @@ _ad:
reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso" reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso"
hide: "Nascondi" hide: "Nascondi"
timezoneinfo: "Il giorno della settimana è determinato in base al fuso orario del server." timezoneinfo: "Il giorno della settimana è determinato in base al fuso orario del server."
adsSettings: "Impostazioni banner"
notesPerOneAd: "Quantità di Note tra i banner"
setZeroToDisable: "Imposta 0 (zero) per disattivare la distribuzione dei banner durante gli aggiornamenti in tempo reale"
adsTooClose: "Attenzione, l'intervallo di pubblicazione dei banner è molto breve, potrebbe infastidire significativamente la fruizione"
_forgotPassword: _forgotPassword:
enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo." enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo."
ifNoEmail: "Se il tuo indirizzo email non risulta registrato, contatta l'amministrazione dell'istanza." ifNoEmail: "Se il tuo indirizzo email non risulta registrato, contatta l'amministrazione dell'istanza."
@@ -1616,7 +1624,7 @@ _menuDisplay:
hide: "Nascondere" hide: "Nascondere"
_wordMute: _wordMute:
muteWords: "Parole da filtrare" muteWords: "Parole da filtrare"
muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con una interruzione di riga, indica la condizione \"O\"" muteWordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
muteWordsDescription2: "Se vuoi indicare delle Espressioni Regolari (regexp), metti la condizione all'interno di due slash (/)" muteWordsDescription2: "Se vuoi indicare delle Espressioni Regolari (regexp), metti la condizione all'interno di due slash (/)"
_instanceMute: _instanceMute:
instanceMuteDescription: "Disattiva tutte le note, le note di rinvio (condivisione) dell'istanza configurata, comprese le risposte agli utenti dell'istanza." instanceMuteDescription: "Disattiva tutte le note, le note di rinvio (condivisione) dell'istanza configurata, comprese le risposte agli utenti dell'istanza."
@@ -1626,7 +1634,7 @@ _instanceMute:
_theme: _theme:
explore: "Esplora temi" explore: "Esplora temi"
install: "Installa un tema" install: "Installa un tema"
manage: "Gerisci temi" manage: "Gestione temi"
code: "Codice tema" code: "Codice tema"
description: "Descrizione" description: "Descrizione"
installed: "{name} è installato" installed: "{name} è installato"
@@ -1681,9 +1689,6 @@ _theme:
infoFg: "Testo di informazioni" infoFg: "Testo di informazioni"
infoWarnBg: "Sfondo degli avvisi" infoWarnBg: "Sfondo degli avvisi"
infoWarnFg: "Testo di avviso" infoWarnFg: "Testo di avviso"
cwBg: "Sfondo del CW"
cwFg: "Testo del pulsante CW"
cwHoverBg: "Sfondo del pulsante CW (sorvolato)"
toastBg: "Sfondo di notifica a comparsa" toastBg: "Sfondo di notifica a comparsa"
toastFg: "Testo di notifica a comparsa" toastFg: "Testo di notifica a comparsa"
buttonBg: "Sfondo del pulsante" buttonBg: "Sfondo del pulsante"
@@ -1878,14 +1883,14 @@ _poll:
remainingSeconds: "Rimangono {s} secondi" remainingSeconds: "Rimangono {s} secondi"
_visibility: _visibility:
public: "Pubblica" public: "Pubblica"
publicDescription: "Visibile per tutti sul Fediverso" publicDescription: "Visibilità pubblica"
home: "Home" home: "Home"
homeDescription: "Visibile solo sulla timeline locale" homeDescription: "Visibile solo nella Home"
followers: "Follower" followers: "Follower"
followersDescription: "Visibile solo ai tuoi follower" followersDescription: "Visibile solo ai tuoi follower"
specified: "Nota diretta" specified: "Nota diretta"
specifiedDescription: "Visibile solo ai profili menzionati" specifiedDescription: "Visibile solo ai profili menzionati"
disableFederation: "Non federare" disableFederation: "Senza federazione"
disableFederationDescription: "Non spedire attività alle altre istanze remote" disableFederationDescription: "Non spedire attività alle altre istanze remote"
_postForm: _postForm:
replyPlaceholder: "Rispondi a questa nota..." replyPlaceholder: "Rispondi a questa nota..."
@@ -1920,6 +1925,7 @@ _exportOrImport:
userLists: "Liste" userLists: "Liste"
excludeMutingUsers: "Escludere gli utenti silenziati" excludeMutingUsers: "Escludere gli utenti silenziati"
excludeInactiveUsers: "Escludere i profili inutilizzati" excludeInactiveUsers: "Escludere i profili inutilizzati"
withReplies: "Includere le risposte da profili importati nella Timeline"
_charts: _charts:
federation: "Federazione" federation: "Federazione"
apRequest: "Richieste" apRequest: "Richieste"
@@ -2084,7 +2090,7 @@ _deck:
list: "Liste" list: "Liste"
channel: "Canale" channel: "Canale"
mentions: "Menzioni" mentions: "Menzioni"
direct: "Diretta" direct: "Note Dirette"
roleTimeline: "Timeline Ruolo" roleTimeline: "Timeline Ruolo"
_dialog: _dialog:
charactersExceeded: "Hai superato il limite di {max} caratteri! ({corrente})" charactersExceeded: "Hai superato il limite di {max} caratteri! ({corrente})"
@@ -2140,3 +2146,11 @@ _moderationLogTypes:
createAd: "Banner creato" createAd: "Banner creato"
deleteAd: "Banner eliminato" deleteAd: "Banner eliminato"
updateAd: "Banner aggiornato" updateAd: "Banner aggiornato"
_fileViewer:
title: "Dettagli del file"
type: "Tipo di file"
size: "Dimensioni file"
url: "URL"
uploadedAt: "Caricato il"
attachedNotes: "Note a cui è allegato"
thisPageCanBeSeenFromTheAuthor: "Questa pagina può essere vista solo da chi ha caricato il file."

View File

@@ -195,6 +195,7 @@ perHour: "1時間ごと"
perDay: "1日ごと" perDay: "1日ごと"
stopActivityDelivery: "アクティビティの配送を停止" stopActivityDelivery: "アクティビティの配送を停止"
blockThisInstance: "このサーバーをブロック" blockThisInstance: "このサーバーをブロック"
silenceThisInstance: "サーバーをサイレンス"
operations: "操作" operations: "操作"
software: "ソフトウェア" software: "ソフトウェア"
version: "バージョン" version: "バージョン"
@@ -213,7 +214,9 @@ clearQueueConfirmText: "未配達の投稿は配送されなくなります。
clearCachedFiles: "キャッシュをクリア" clearCachedFiles: "キャッシュをクリア"
clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?" clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?"
blockedInstances: "ブロックしたサーバー" blockedInstances: "ブロックしたサーバー"
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このサーバーとやり取りできなくなります。サブドメインもブロックされます。" blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。"
silencedInstances: "サイレンスしたサーバー"
silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。"
muteAndBlock: "ミュートとブロック" muteAndBlock: "ミュートとブロック"
mutedUsers: "ミュートしたユーザー" mutedUsers: "ミュートしたユーザー"
blockedUsers: "ブロックしたユーザー" blockedUsers: "ブロックしたユーザー"
@@ -531,6 +534,7 @@ serverLogs: "サーバーログ"
deleteAll: "全て削除" deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する" showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)" showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)"
withRepliesByDefaultForNewlyFollowed: "フォローする際、デフォルトで返信をTLに含むようにする"
newNoteRecived: "新しいノートがあります" newNoteRecived: "新しいノートがあります"
sounds: "サウンド" sounds: "サウンド"
sound: "サウンド" sound: "サウンド"
@@ -589,7 +593,7 @@ poll: "アンケート"
useCw: "内容を隠す" useCw: "内容を隠す"
enablePlayer: "プレイヤーを開く" enablePlayer: "プレイヤーを開く"
disablePlayer: "プレイヤーを閉じる" disablePlayer: "プレイヤーを閉じる"
expandTweet: "ツイートを展開する" expandTweet: "ポストを展開する"
themeEditor: "テーマエディター" themeEditor: "テーマエディター"
description: "説明" description: "説明"
describeFile: "キャプションを付ける" describeFile: "キャプションを付ける"
@@ -794,7 +798,7 @@ active: "アクティブ"
offline: "オフライン" offline: "オフライン"
notRecommended: "非推奨" notRecommended: "非推奨"
botProtection: "Botプロテクション" botProtection: "Botプロテクション"
instanceBlocking: "サーバーブロック" instanceBlocking: "サーバーブロック・サイレンス"
selectAccount: "アカウントを選択" selectAccount: "アカウントを選択"
switchAccount: "アカウントを切り替え" switchAccount: "アカウントを切り替え"
enabled: "有効" enabled: "有効"
@@ -1714,9 +1718,6 @@ _theme:
infoFg: "情報の文字" infoFg: "情報の文字"
infoWarnBg: "警告の背景" infoWarnBg: "警告の背景"
infoWarnFg: "警告の文字" infoWarnFg: "警告の文字"
cwBg: "CW ボタンの背景"
cwFg: "CW ボタンの文字"
cwHoverBg: "CW ボタンの背景 (ホバー)"
toastBg: "通知トーストの背景" toastBg: "通知トーストの背景"
toastFg: "通知トーストの文字" toastFg: "通知トーストの文字"
buttonBg: "ボタンの背景" buttonBg: "ボタンの背景"
@@ -1969,6 +1970,7 @@ _exportOrImport:
userLists: "リスト" userLists: "リスト"
excludeMutingUsers: "ミュートしているユーザーを除外" excludeMutingUsers: "ミュートしているユーザーを除外"
excludeInactiveUsers: "使われていないアカウントを除外" excludeInactiveUsers: "使われていないアカウントを除外"
withReplies: "インポートした人による返信をTLに含むようにする"
_charts: _charts:
federation: "連合" federation: "連合"
@@ -2206,3 +2208,12 @@ _moderationLogTypes:
createAd: "広告を作成" createAd: "広告を作成"
deleteAd: "広告を削除" deleteAd: "広告を削除"
updateAd: "広告を更新" updateAd: "広告を更新"
_fileViewer:
title: "ファイルの詳細"
type: "ファイルタイプ"
size: "ファイルサイズ"
url: "URL"
uploadedAt: "追加日"
attachedNotes: "添付されているノート"
thisPageCanBeSeenFromTheAuthor: "このページは、このファイルをアップロードしたユーザーしか閲覧できません。"

View File

@@ -45,6 +45,7 @@ pin: "ピン留めしとく"
unpin: "やっぱピン留めせん" unpin: "やっぱピン留めせん"
copyContent: "内容をコピー" copyContent: "内容をコピー"
copyLink: "リンクをコピー" copyLink: "リンクをコピー"
copyLinkRenote: "リノートのリンクをコピーするで?"
delete: "ほかす" delete: "ほかす"
deleteAndEdit: "ほかして直す" deleteAndEdit: "ほかして直す"
deleteAndEditConfirm: "このートをほかしてもっかい直すこのートへのツッコミ、Renote、返信も全部消えるんやけどそれでもええん" deleteAndEditConfirm: "このートをほかしてもっかい直すこのートへのツッコミ、Renote、返信も全部消えるんやけどそれでもええん"
@@ -194,6 +195,7 @@ perHour: "1時間ごと"
perDay: "1日ごと" perDay: "1日ごと"
stopActivityDelivery: "アクティビティの配送をやめる" stopActivityDelivery: "アクティビティの配送をやめる"
blockThisInstance: "このサーバーをブロックすんで" blockThisInstance: "このサーバーをブロックすんで"
silenceThisInstance: "サーバーサイレンスすんで?"
operations: "操作" operations: "操作"
software: "ソフトウェア" software: "ソフトウェア"
version: "バージョン" version: "バージョン"
@@ -213,6 +215,8 @@ clearCachedFiles: "キャッシュをほかす"
clearCachedFilesConfirm: "キャッシュされとるリモートファイルをみんなほかしてええか?" clearCachedFilesConfirm: "キャッシュされとるリモートファイルをみんなほかしてええか?"
blockedInstances: "ブロックしたサーバー" blockedInstances: "ブロックしたサーバー"
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定してな。ブロックされてもうたサーバーとはもう金輪際やり取りできひんくなるで。ついでにそのサブドメインもブロックするで。" blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定してな。ブロックされてもうたサーバーとはもう金輪際やり取りできひんくなるで。ついでにそのサブドメインもブロックするで。"
silencedInstances: "サーバーサイレンスされてんねん"
silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定すんで。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなんねん。ブロックしたインスタンスには影響せーへんで。"
muteAndBlock: "ミュートとブロック" muteAndBlock: "ミュートとブロック"
mutedUsers: "ミュートしたユーザー" mutedUsers: "ミュートしたユーザー"
blockedUsers: "ブロックしたユーザー" blockedUsers: "ブロックしたユーザー"
@@ -410,12 +414,14 @@ aboutMisskey: "Misskeyってなんや"
administrator: "管理者" administrator: "管理者"
token: "トークン" token: "トークン"
2fa: "二要素認証" 2fa: "二要素認証"
setupOf2fa: "二要素認証のセットアップ"
totp: "認証アプリ" totp: "認証アプリ"
totpDescription: "認証アプリ使うてワンタイムパスワードを入れる" totpDescription: "認証アプリ使うてワンタイムパスワードを入れる"
moderator: "モデレーター" moderator: "モデレーター"
moderation: "モデレーション" moderation: "モデレーション"
moderationNote: "モデレーションノート" moderationNote: "モデレーションノート"
addModerationNote: "モデレーションノートを追加するで" addModerationNote: "モデレーションノートを追加するで"
moderationLogs: "モデログ"
nUsersMentioned: "{n}人が投稿" nUsersMentioned: "{n}人が投稿"
securityKeyAndPasskey: "セキュリティキー・パスキー" securityKeyAndPasskey: "セキュリティキー・パスキー"
securityKey: "セキュリティキー" securityKey: "セキュリティキー"
@@ -528,6 +534,7 @@ serverLogs: "サーバーログ"
deleteAll: "全部ほかす" deleteAll: "全部ほかす"
showFixedPostForm: "タイムラインの上の方で投稿できるようにやってくれへん?" showFixedPostForm: "タイムラインの上の方で投稿できるようにやってくれへん?"
showFixedPostFormInChannel: "タイムラインの上の方で投稿できるようにするわ(チャンネル)" showFixedPostFormInChannel: "タイムラインの上の方で投稿できるようにするわ(チャンネル)"
withRepliesByDefaultForNewlyFollowed: "フォローする時、デフォルトで返信をタイムラインに含むようにしよか"
newNoteRecived: "新しいノートがあるで" newNoteRecived: "新しいノートがあるで"
sounds: "サウンド" sounds: "サウンド"
sound: "サウンド" sound: "サウンド"
@@ -586,7 +593,7 @@ poll: "アンケート"
useCw: "内容を隠す" useCw: "内容を隠す"
enablePlayer: "プレイヤーを開く" enablePlayer: "プレイヤーを開く"
disablePlayer: "プレイヤーを閉じる" disablePlayer: "プレイヤーを閉じる"
expandTweet: "ツイートを展開する" expandTweet: "ポストを展開する"
themeEditor: "テーマエディター" themeEditor: "テーマエディター"
description: "説明" description: "説明"
describeFile: "キャプションを付ける" describeFile: "キャプションを付ける"
@@ -655,6 +662,7 @@ behavior: "動作"
sample: "サンプル" sample: "サンプル"
abuseReports: "通報" abuseReports: "通報"
reportAbuse: "通報" reportAbuse: "通報"
reportAbuseRenote: "リノート苦情だすで?"
reportAbuseOf: "{name}を通報する" reportAbuseOf: "{name}を通報する"
fillAbuseReportDescription: "細かい通報理由を書いてなー。対象ートがある時はそのURLも書いといてなー。" fillAbuseReportDescription: "細かい通報理由を書いてなー。対象ートがある時はそのURLも書いといてなー。"
abuseReported: "無事内容が送信されたみたいやで。おおきに〜。" abuseReported: "無事内容が送信されたみたいやで。おおきに〜。"
@@ -707,6 +715,7 @@ lockedAccountInfo: "フォローを承認制にしとっても、ノートの公
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで" alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで"
loadRawImages: "添付画像のサムネイルをオリジナル画質にするで" loadRawImages: "添付画像のサムネイルをオリジナル画質にするで"
disableShowingAnimatedImages: "アニメーション画像を再生せんとくで" disableShowingAnimatedImages: "アニメーション画像を再生せんとくで"
highlightSensitiveMedia: "メディアがセンシティブなことをめっっちゃわかりやすく表紙"
verificationEmailSent: "無事確認のメールを送れたで。メールに書いてあるリンクにアクセスして、設定を完了してなー。" verificationEmailSent: "無事確認のメールを送れたで。メールに書いてあるリンクにアクセスして、設定を完了してなー。"
notSet: "未設定" notSet: "未設定"
emailVerified: "メールアドレスは確認されたで" emailVerified: "メールアドレスは確認されたで"
@@ -1021,6 +1030,7 @@ retryAllQueuesConfirmText: "一時的にサーバー重なるかもしれへん
enableChartsForRemoteUser: "リモートユーザーのチャートを作る" enableChartsForRemoteUser: "リモートユーザーのチャートを作る"
enableChartsForFederatedInstances: "リモートサーバーのチャートを作る" enableChartsForFederatedInstances: "リモートサーバーのチャートを作る"
showClipButtonInNoteFooter: "ノートのアクションにクリップを追加" showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
reactionsDisplaySize: "リアクションの表示のでかさ"
noteIdOrUrl: "ートIDかURL" noteIdOrUrl: "ートIDかURL"
video: "動画" video: "動画"
videos: "動画" videos: "動画"
@@ -1107,8 +1117,28 @@ replies: "返事"
renotes: "Renote" renotes: "Renote"
loadReplies: "返信を見るで" loadReplies: "返信を見るで"
loadConversation: "会話を見るで" loadConversation: "会話を見るで"
pinnedList: "ピン留めしはったリスト"
keepScreenOn: "デバイスの画面を常にオンにすんで"
verifiedLink: "このリンク先の所有者であることが確認されたで。" verifiedLink: "このリンク先の所有者であることが確認されたで。"
notifyNotes: "投稿を通知"
unnotifyNotes: "投稿の通知を解除すんで"
authentication: "認証"
authenticationRequiredToContinue: "続けるには認証をやってや。" authenticationRequiredToContinue: "続けるには認証をやってや。"
dateAndTime: "日時"
showRenotes: "リノートを表示"
edited: "編集し終わってる"
notificationRecieveConfig: "通知を受け取るかの設定"
mutualFollow: "お互いフォローしてんで"
fileAttachedOnly: "ファイル付きのみ"
showRepliesToOthersInTimeline: "タイムラインに他の人への返信とかも含めんで"
hideRepliesToOthersInTimeline: "タイムラインに他の人への返信とかは見ーへんで"
externalServices: "他のサイトのサービス"
impressum: "運営者の情報"
impressumUrl: "運営者の情報URL"
impressumDescription: "ドイツなどのほんま1部の国と地域ではな、表示が義務付けられててん。(Impressum)"
privacyPolicy: "プライバシーポリシー"
privacyPolicyUrl: "プライバシーポリシーURL"
tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
_announcement: _announcement:
forExistingUsers: "もうおるユーザーのみ" forExistingUsers: "もうおるユーザーのみ"
forExistingUsersDescription: "有効にすると、このお知らせ作成時点でおるユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。" forExistingUsersDescription: "有効にすると、このお知らせ作成時点でおるユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。"
@@ -1141,6 +1171,8 @@ _serverSettings:
appIconUsageExample: "PWAや、スマートフォンのホーム画面にブックマークとして追加された時など" appIconUsageExample: "PWAや、スマートフォンのホーム画面にブックマークとして追加された時など"
appIconStyleRecommendation: "円形もしくは角丸にクロップされる場合があるさかいに、塗り潰された余白のある背景があるものが推奨されるで。" appIconStyleRecommendation: "円形もしくは角丸にクロップされる場合があるさかいに、塗り潰された余白のある背景があるものが推奨されるで。"
appIconResolutionMustBe: "解像度は必ず{resolution}である必要があるで。" appIconResolutionMustBe: "解像度は必ず{resolution}である必要があるで。"
manifestJsonOverride: "manifest.jsonのオーバーライド"
shortName: "略称"
shortNameDescription: "サーバーの名前が長い時に、代わりに表示することのできるあだ名。" shortNameDescription: "サーバーの名前が長い時に、代わりに表示することのできるあだ名。"
_accountMigration: _accountMigration:
moveFrom: "別のアカウントからこのアカウントに引っ越す" moveFrom: "別のアカウントからこのアカウントに引っ越す"
@@ -1396,6 +1428,9 @@ _achievements:
title: "Brain Diver" title: "Brain Diver"
description: "Brain Diverへのリンクを投稿したった" description: "Brain Diverへのリンクを投稿したった"
flavor: "Misskey-Misskey La-Tu-Ma" flavor: "Misskey-Misskey La-Tu-Ma"
_smashTestNotificationButton:
title: "テスト過剰"
description: "通知テストをごく短時間のうちに連続して行ったねん"
_role: _role:
new: "ロールの作成" new: "ロールの作成"
edit: "ロールの編集" edit: "ロールの編集"
@@ -1453,6 +1488,7 @@ _role:
descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩なって、大きいほど制限されるで。" descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩なって、大きいほど制限されるで。"
canHideAds: "広告を表示させへん" canHideAds: "広告を表示させへん"
canSearchNotes: "ノート検索を使わすかどうか" canSearchNotes: "ノート検索を使わすかどうか"
canUseTranslator: "翻訳機能の利用"
_condition: _condition:
isLocal: "ローカルユーザー" isLocal: "ローカルユーザー"
isRemote: "リモートユーザー" isRemote: "リモートユーザー"
@@ -1501,6 +1537,10 @@ _ad:
reduceFrequencyOfThisAd: "この広告の表示頻度を下げるで" reduceFrequencyOfThisAd: "この広告の表示頻度を下げるで"
hide: "表示せん" hide: "表示せん"
timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されるで。" timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されるで。"
adsSettings: "広告配信設定"
notesPerOneAd: "リアタイ更新中に広告を出す間隔(ノートの個数な)"
setZeroToDisable: "0でリアタイ更新時の広告配信を無効にすんで"
adsTooClose: "広告を出す間隔がめっちゃ短いから、ユーザー体験が著しく損なわれる可能性があんで。"
_forgotPassword: _forgotPassword:
enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。" enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。"
ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。" ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。"
@@ -1649,9 +1689,6 @@ _theme:
infoFg: "情報の文字" infoFg: "情報の文字"
infoWarnBg: "警告の背景" infoWarnBg: "警告の背景"
infoWarnFg: "警告の文字" infoWarnFg: "警告の文字"
cwBg: "CW ボタンの背景"
cwFg: "CW ボタンの文字"
cwHoverBg: "CW ボタンの背景 (ホバー)"
toastBg: "通知トーストの背景" toastBg: "通知トーストの背景"
toastFg: "通知トーストの文字" toastFg: "通知トーストの文字"
buttonBg: "ボタンの背景" buttonBg: "ボタンの背景"
@@ -1703,6 +1740,7 @@ _2fa:
step1: "ほんなら、{a}や{b}とかの認証アプリを使っとるデバイスにインストールしてな。" step1: "ほんなら、{a}や{b}とかの認証アプリを使っとるデバイスにインストールしてな。"
step2: "次に、ここにあるQRコードをアプリでスキャンしてな。" step2: "次に、ここにあるQRコードをアプリでスキャンしてな。"
step2Click: "QRコードをクリックすると、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。" step2Click: "QRコードをクリックすると、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。"
step2Uri: "デスクトップアプリを使う時は次のURIを入れるで"
step3Title: "確認コードを入れてーや" step3Title: "確認コードを入れてーや"
step3: "アプリに表示されているトークンを入力して終わりや。" step3: "アプリに表示されているトークンを入力して終わりや。"
setupCompleted: "設定が完了したで。" setupCompleted: "設定が完了したで。"
@@ -1721,6 +1759,7 @@ _2fa:
renewTOTPOk: "もっかい設定する" renewTOTPOk: "もっかい設定する"
renewTOTPCancel: "やめとく" renewTOTPCancel: "やめとく"
checkBackupCodesBeforeCloseThisWizard: "このウィザードを閉じる前に、したのバックアップコードを確認しいや。" checkBackupCodesBeforeCloseThisWizard: "このウィザードを閉じる前に、したのバックアップコードを確認しいや。"
backupCodes: "バックアップコード"
backupCodesDescription: "認証アプリが使用できんなった場合、以下のバックアップコードを使ってアカウントにアクセスできるで。これらのコードは必ず安全な場所に置いときや。各コードは一回だけ使用できるで。" backupCodesDescription: "認証アプリが使用できんなった場合、以下のバックアップコードを使ってアカウントにアクセスできるで。これらのコードは必ず安全な場所に置いときや。各コードは一回だけ使用できるで。"
backupCodeUsedWarning: "バックアップコードが使用されたで。認証アプリが使えなくなってるん場合、なるべく早く認証アプリを再設定しや。" backupCodeUsedWarning: "バックアップコードが使用されたで。認証アプリが使えなくなってるん場合、なるべく早く認証アプリを再設定しや。"
backupCodesExhaustedWarning: "バックアップコードが全て使用されたで。認証アプリを利用できん場合、これ以上アカウントにアクセスできなくなるで。認証アプリを再登録しや。" backupCodesExhaustedWarning: "バックアップコードが全て使用されたで。認証アプリを利用できん場合、これ以上アカウントにアクセスできなくなるで。認証アプリを再登録しや。"
@@ -1776,6 +1815,7 @@ _antennaSources:
homeTimeline: "フォローしとるユーザーのノート" homeTimeline: "フォローしとるユーザーのノート"
users: "選らんだ一人か複数のユーザーのノート" users: "選らんだ一人か複数のユーザーのノート"
userList: "選んだリストのユーザーのノート" userList: "選んだリストのユーザーのノート"
userBlacklist: "選んだ1人か複数のユーザーのノート"
_weekday: _weekday:
sunday: "日曜日" sunday: "日曜日"
monday: "月曜日" monday: "月曜日"
@@ -1875,6 +1915,7 @@ _profile:
metadataContent: "内容" metadataContent: "内容"
changeAvatar: "アバター画像を変更するで" changeAvatar: "アバター画像を変更するで"
changeBanner: "バナー画像を変更するで" changeBanner: "バナー画像を変更するで"
verifiedLinkDescription: "内容をURLに設定すると、リンク先のwebサイトに自分のプロフのリンクが含まれてる場合に所有者確認済みアイコンを表示させることができるで。"
_exportOrImport: _exportOrImport:
allNotes: "全てのノート" allNotes: "全てのノート"
favoritedNotes: "お気に入りにしたノート" favoritedNotes: "お気に入りにしたノート"
@@ -1884,6 +1925,7 @@ _exportOrImport:
userLists: "リスト" userLists: "リスト"
excludeMutingUsers: "ミュートしてるユーザーは入れんとくわ" excludeMutingUsers: "ミュートしてるユーザーは入れんとくわ"
excludeInactiveUsers: "使われてなさそうなアカウントは入れんとくわ" excludeInactiveUsers: "使われてなさそうなアカウントは入れんとくわ"
withReplies: "インポートした人による返信をTLに含むようにすんで。"
_charts: _charts:
federation: "連合" federation: "連合"
apRequest: "リクエスト" apRequest: "リクエスト"
@@ -1993,14 +2035,17 @@ _notification:
youReceivedFollowRequest: "フォロー許可してほしいみたいやな" youReceivedFollowRequest: "フォロー許可してほしいみたいやな"
yourFollowRequestAccepted: "フォローさせてもろたで" yourFollowRequestAccepted: "フォローさせてもろたで"
pollEnded: "アンケートの結果が出たみたいや" pollEnded: "アンケートの結果が出たみたいや"
newNote: "さらの投稿"
unreadAntennaNote: "アンテナ {name}" unreadAntennaNote: "アンテナ {name}"
emptyPushNotificationMessage: "プッシュ通知の更新をしといたで" emptyPushNotificationMessage: "プッシュ通知の更新をしといたで"
achievementEarned: "実績を獲得しとるで" achievementEarned: "実績を獲得しとるで"
testNotification: "通知テスト"
checkNotificationBehavior: "通知の表示を確かめるで" checkNotificationBehavior: "通知の表示を確かめるで"
sendTestNotification: "テスト通知を送信するで" sendTestNotification: "テスト通知を送信するで"
notificationWillBeDisplayedLikeThis: "通知はこのように表示されるで" notificationWillBeDisplayedLikeThis: "通知はこのように表示されるで"
_types: _types:
all: "すべて" all: "すべて"
note: "あんたらの新規投稿"
follow: "フォロー" follow: "フォロー"
mention: "メンション" mention: "メンション"
reply: "リプライ" reply: "リプライ"
@@ -2035,6 +2080,7 @@ _deck:
widgetsIntroduction: "カラムのメニューから、「ウィジェットの編集」を選んでウィジェットを追加してなー" widgetsIntroduction: "カラムのメニューから、「ウィジェットの編集」を選んでウィジェットを追加してなー"
useSimpleUiForNonRootPages: "非ルートページは簡易UIで表示" useSimpleUiForNonRootPages: "非ルートページは簡易UIで表示"
usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となるで" usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となるで"
flexible: "幅を自動調整"
_columns: _columns:
main: "メイン" main: "メイン"
widgets: "ウィジェット" widgets: "ウィジェット"
@@ -2070,6 +2116,41 @@ _webhookSettings:
reaction: "ツッコミがあるとき~!" reaction: "ツッコミがあるとき~!"
mention: "メンションがあるとき~!" mention: "メンションがあるとき~!"
_moderationLogTypes: _moderationLogTypes:
createRole: "ロールを追加すんで"
deleteRole: "ロールほかす"
updateRole: "ロールの更新すんで"
assignRole: "ロールへアサイン"
unassignRole: "ロールのアサインほかす"
suspend: "凍結" suspend: "凍結"
unsuspend: "凍結解除"
addCustomEmoji: "自由な絵文字追加されたで"
updateCustomEmoji: "自由な絵文字更新されたで"
deleteCustomEmoji: "自由な絵文字消されたで"
updateServerSettings: "サーバー設定更新すんねん"
updateUserNote: "モデレーションノート更新"
deleteDriveFile: "ファイルをほかす"
deleteNote: "ノートを削除"
createGlobalAnnouncement: "みんなへの通告を作成したで"
createUserAnnouncement: "あんたらへの通告を作成したで"
updateGlobalAnnouncement: "みんなへの通告更新したったで"
updateUserAnnouncement: "あんたらへの通告更新したったで"
deleteGlobalAnnouncement: "みんなへの通告消したったで"
deleteUserAnnouncement: "あんたらへのお知らせを削除"
resetPassword: "パスワードをリセット" resetPassword: "パスワードをリセット"
suspendRemoteInstance: "リモートサーバーを止めんで"
unsuspendRemoteInstance: "リモートサーバーを再開すんで"
markSensitiveDriveFile: "ファイルをセンシティブ付与"
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
resolveAbuseReport: "苦情を解決"
createInvitation: "招待コードを作成" createInvitation: "招待コードを作成"
createAd: "広告を作んで"
deleteAd: "広告ほかす"
updateAd: "広告を更新"
_fileViewer:
title: "ファイルの詳しい情報"
type: "ファイルの種類"
size: "ファイルのでかさ"
url: "URL"
uploadedAt: "追加した日"
attachedNotes: "ファイルがついてきてるノート"
thisPageCanBeSeenFromTheAuthor: "このページはこのファイルをアップした人しか見れへんねん。"

View File

@@ -589,7 +589,7 @@ poll: "투표"
useCw: "내용 숨기기" useCw: "내용 숨기기"
enablePlayer: "플레이어 열기" enablePlayer: "플레이어 열기"
disablePlayer: "플레이어 닫기" disablePlayer: "플레이어 닫기"
expandTweet: "트윗 확장하기" expandTweet: "게시물 확장하기"
themeEditor: "테마 에디터" themeEditor: "테마 에디터"
description: "설명" description: "설명"
describeFile: "캡션 추가" describeFile: "캡션 추가"
@@ -1663,9 +1663,6 @@ _theme:
infoFg: "정보창 텍스트" infoFg: "정보창 텍스트"
infoWarnBg: "경고창 배경" infoWarnBg: "경고창 배경"
infoWarnFg: "경고창 텍스트" infoWarnFg: "경고창 텍스트"
cwBg: "CW 버튼 배경"
cwFg: "CW 버튼 텍스트"
cwHoverBg: "CW 버튼 배경 (호버)"
toastBg: "알림창 배경" toastBg: "알림창 배경"
toastFg: "알림창 텍스트" toastFg: "알림창 텍스트"
buttonBg: "버튼 배경" buttonBg: "버튼 배경"

View File

@@ -1043,9 +1043,6 @@ _theme:
infoFg: "Tekst informacji" infoFg: "Tekst informacji"
infoWarnBg: "Tło ostrzeżenia" infoWarnBg: "Tło ostrzeżenia"
infoWarnFg: "Tekst ostrzeżenia" infoWarnFg: "Tekst ostrzeżenia"
cwBg: "Tło CW"
cwFg: "Tekst CW"
cwHoverBg: "Tło CW (po najechaniu)"
toastBg: "Tło powiadomień" toastBg: "Tło powiadomień"
toastFg: "Tekst powiadomień" toastFg: "Tekst powiadomień"
buttonBg: "Tło przycisku" buttonBg: "Tło przycisku"

View File

@@ -1551,9 +1551,6 @@ _theme:
infoFg: "Текст сообщения" infoFg: "Текст сообщения"
infoWarnBg: "Фон предупреждения" infoWarnBg: "Фон предупреждения"
infoWarnFg: "Текст предупреждения" infoWarnFg: "Текст предупреждения"
cwBg: "Фон предупреждения о содержимом"
cwFg: "Текст предупреждения о содержимом"
cwHoverBg: "Фон предупреждения о содержимом (под указателем)"
toastBg: "Фон оповещения" toastBg: "Фон оповещения"
toastFg: "Текст оповещения" toastFg: "Текст оповещения"
buttonBg: "Фон кнопки" buttonBg: "Фон кнопки"

View File

@@ -1102,9 +1102,6 @@ _theme:
infoFg: "Informačný text" infoFg: "Informačný text"
infoWarnBg: "Pozadie varovania" infoWarnBg: "Pozadie varovania"
infoWarnFg: "Text varovania" infoWarnFg: "Text varovania"
cwBg: "CW pozadie tlačidla"
cwFg: "CW text tlačidla"
cwHoverBg: "CW pozadie tlačidla (pod kurzorom)"
toastBg: "Pozadie upozornenia" toastBg: "Pozadie upozornenia"
toastFg: "Text upozornenia" toastFg: "Text upozornenia"
buttonBg: "Pozadie tlačidla" buttonBg: "Pozadie tlačidla"

View File

@@ -1117,15 +1117,25 @@ keepScreenOn: "เปิดหน้าจอไว้"
notifyNotes: "แจ้งเตือนเกี่ยวกับโพสต์ใหม่" notifyNotes: "แจ้งเตือนเกี่ยวกับโพสต์ใหม่"
unnotifyNotes: "หยุดการแจ้งเตือนเกี่ยวกับโน้ตใหม่" unnotifyNotes: "หยุดการแจ้งเตือนเกี่ยวกับโน้ตใหม่"
authentication: "การตรวจสอบสิทธิ์" authentication: "การตรวจสอบสิทธิ์"
authenticationRequiredToContinue: "กรุณาตรวจสอบการรับรองความถูกต้องเพื่อดำเนินการต่อ"
dateAndTime: "เวลาประทับ" dateAndTime: "เวลาประทับ"
showRenotes: "แสดงรีโน้ต" showRenotes: "แสดงรีโน้ต"
edited: "แก้ไขแล้ว" edited: "แก้ไขแล้ว"
notificationRecieveConfig: "การตั้งค่าการแจ้งเตือน" notificationRecieveConfig: "การตั้งค่าการแจ้งเตือน"
mutualFollow: "ติดตามซึ่งกันและกัน" mutualFollow: "ติดตามซึ่งกันและกัน"
fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น" fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น"
showRepliesToOthersInTimeline: "แสดงการตอบกลับไปยังอื่นๆในไทม์ไลน์"
hideRepliesToOthersInTimeline: "ซ่อนการตอบกลับไปยังอื่นๆจากไทม์ไลน์"
externalServices: "บริการภายนอก"
impressum: "อิมเพรสชั่น"
impressumUrl: "URL อิมเพรสชั่น"
privacyPolicy: "นโยบายความเป็นส่วนตัว"
privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว"
tosAndPrivacyPolicy: "เงื่อนไขในการให้บริการและนโยบายความเป็นส่วนตัว"
_announcement: _announcement:
forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น" forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น"
forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน" forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
needConfirmationToRead: "จำเป็นต้องยืนยันเพื่อทำเครื่องหมายบอกว่าอ่านแล้ว"
needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\"" needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\""
end: "ประกาศเก็บถาวร" end: "ประกาศเก็บถาวร"
tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ" tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ"
@@ -1150,6 +1160,8 @@ _serverRules:
description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ" description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ"
_serverSettings: _serverSettings:
iconUrl: "ไอคอน URL" iconUrl: "ไอคอน URL"
appIconUsageExample: "E.g. เป็น PWA หรือเมื่อแสดงผลเป็นบุ๊กมาร์กหน้าจอหลักบนโทรศัพท์"
appIconResolutionMustBe: "ความละเอียดขั้นต่ำไว้คือ {resolution}."
manifestJsonOverride: "manifest.json โอเวอร์ลาย" manifestJsonOverride: "manifest.json โอเวอร์ลาย"
shortName: "ชื่อย่อ" shortName: "ชื่อย่อ"
_accountMigration: _accountMigration:
@@ -1515,6 +1527,8 @@ _ad:
reduceFrequencyOfThisAd: "แสดงโฆษณานี้ให้น้อยลง" reduceFrequencyOfThisAd: "แสดงโฆษณานี้ให้น้อยลง"
hide: "ไม่ต้องแสดง" hide: "ไม่ต้องแสดง"
timezoneinfo: "วันในสัปดาห์นี้จะถูกกำหนดจากโซนเวลาของเซิร์ฟเวอร์" timezoneinfo: "วันในสัปดาห์นี้จะถูกกำหนดจากโซนเวลาของเซิร์ฟเวอร์"
adsSettings: "ตั้งค่าการโฆษณา"
setZeroToDisable: "ตั้งค่านี้ให้เป็น 0 เพื่อปิดใช้งานโฆษณาอัปเดตแบบเรียลไทม์"
_forgotPassword: _forgotPassword:
enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ" enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ"
ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ" ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ"
@@ -1663,9 +1677,6 @@ _theme:
infoFg: "ข้อความข้อมูล" infoFg: "ข้อความข้อมูล"
infoWarnBg: "คำเตือนพื้นหลัง" infoWarnBg: "คำเตือนพื้นหลัง"
infoWarnFg: "คำเตือนข้อความ" infoWarnFg: "คำเตือนข้อความ"
cwBg: "ปุ่ม CW พื้นหลัง"
cwFg: "ปุ่ม CW ข้อความ"
cwHoverBg: "ปุ่ม CW พื้นหลัง (โฮเวอร์)"
toastBg: "ประวัติการแจ้งเตือน" toastBg: "ประวัติการแจ้งเตือน"
toastFg: "ข้อความแจ้งเตือน" toastFg: "ข้อความแจ้งเตือน"
buttonBg: "ปุ่มพื้นหลัง" buttonBg: "ปุ่มพื้นหลัง"
@@ -1717,6 +1728,7 @@ _2fa:
step1: "ขั้นตอนแรก ติดตั้งแอปยืนยันตัวตน (เช่น {a} หรือ {b}) บนอุปกรณ์ของคุณ" step1: "ขั้นตอนแรก ติดตั้งแอปยืนยันตัวตน (เช่น {a} หรือ {b}) บนอุปกรณ์ของคุณ"
step2: "จากนั้นสแกนรหัส QR ที่แสดงบนหน้าจอนี้" step2: "จากนั้นสแกนรหัส QR ที่แสดงบนหน้าจอนี้"
step2Click: "การคลิกที่รหัส QR นี้จะช่วยให้คุณนั้นสามารถลงทะเบียน 2FA กับคีย์ความปลอดภัยหรือแอปตรวจสอบความถูกต้องของโทรศัพท์ได้" step2Click: "การคลิกที่รหัส QR นี้จะช่วยให้คุณนั้นสามารถลงทะเบียน 2FA กับคีย์ความปลอดภัยหรือแอปตรวจสอบความถูกต้องของโทรศัพท์ได้"
step2Uri: "ป้อนใส่ URL ดังต่อไปนี้ถ้าหากคุณใช้โปรแกรมเดสก์ท็อป"
step3Title: "ป้อนรหัสยืนยัน" step3Title: "ป้อนรหัสยืนยัน"
step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า" step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า"
setupCompleted: "ตั้งค่าสำเร็จแล้ว" setupCompleted: "ตั้งค่าสำเร็จแล้ว"
@@ -1735,6 +1747,8 @@ _2fa:
renewTOTPOk: "ตั้งค่าคอนฟิกใหม่" renewTOTPOk: "ตั้งค่าคอนฟิกใหม่"
renewTOTPCancel: "ไม่เป็นไร" renewTOTPCancel: "ไม่เป็นไร"
backupCodes: "รหัสสำรองข้อมูล" backupCodes: "รหัสสำรองข้อมูล"
backupCodeUsedWarning: "มีการใช้รหัสสำรองแล้ว โปรดกรุณากำหนดค่าการตรวจสอบสิทธิ์แบบสองปัจจัยโดยเร็วที่สุดถ้าหากคุณยังไม่สามารถใช้งานได้อีก"
backupCodesExhaustedWarning: "รหัสสำรองทั้งหมดถูกใช้แล้ว ถ้าหากคุณยังสูญเสียการเข้าถึงแอปการตรวจสอบสิทธิ์แบบสองปัจจัยคุณจะยังไม่สามารถเข้าถึงบัญชีนี้ได้ กรุณากำหนดค่าการรับรองความถูกต้องด้วยการยืนยันสองชั้น"
_permissions: _permissions:
"read:account": "ดูข้อมูลบัญชีของคุณ" "read:account": "ดูข้อมูลบัญชีของคุณ"
"write:account": "แก้ไขข้อมูลบัญชีของคุณ" "write:account": "แก้ไขข้อมูลบัญชีของคุณ"
@@ -1897,6 +1911,7 @@ _exportOrImport:
userLists: "รายการ" userLists: "รายการ"
excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง" excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง"
excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน" excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน"
withReplies: "รวมการตอบกลับจากผู้ใช้ที่นำเข้าไว้ในไทม์ไลน์"
_charts: _charts:
federation: "สหพันธ์" federation: "สหพันธ์"
apRequest: "คำขอ" apRequest: "คำขอ"
@@ -2117,3 +2132,11 @@ _moderationLogTypes:
createAd: "สร้างโฆษณาแล้ว" createAd: "สร้างโฆษณาแล้ว"
deleteAd: "ลบโฆษณาออกแล้ว" deleteAd: "ลบโฆษณาออกแล้ว"
updateAd: "อัปเดตโฆษณาแล้ว" updateAd: "อัปเดตโฆษณาแล้ว"
_fileViewer:
title: "รายละเอียดไฟล์"
type: "ประเภทไฟล์"
size: "ขนาดไฟล์"
url: "URL"
uploadedAt: "วันที่เข้าร่วม"
attachedNotes: "โน้ตที่แนบมาด้วย"
thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น"

View File

@@ -1,4 +1,19 @@
--- ---
_lang_: "ياپونچە" _lang_: "ياپونچە"
headlineMisskey: "خاتىرە ئارقىلىق ئۇلانغان تور"
monthAndDay: "{day}-{month}"
search: "ئىزدەش" search: "ئىزدەش"
ok: "ماقۇل"
noThankYou: "ئۇنى توختىتىڭ"
profile: "profile"
login: "كىرىش"
loggingIn: "كىرىش"
pin: "pinned"
delete: "ئۆچۈرۈش"
pinned: "pinned"
remove: "ئۆچۈرۈش"
searchByGoogle: "ئىزدەش" searchByGoogle: "ئىزدەش"
_2fa:
renewTOTPCancel: "ئۇنى توختىتىڭ"
_widgets:
profile: "profile"

View File

@@ -1290,9 +1290,6 @@ _theme:
infoFg: "Текст інформації" infoFg: "Текст інформації"
infoWarnBg: "Фон попередження" infoWarnBg: "Фон попередження"
infoWarnFg: "Текст попередження" infoWarnFg: "Текст попередження"
cwBg: "Фон чутливого змісту"
cwFg: "Текст чутливого змісту"
cwHoverBg: "Фон чутливого змісту (при наведенні)"
toastBg: "Фон повідомлення" toastBg: "Фон повідомлення"
toastFg: "Текст повідомлення" toastFg: "Текст повідомлення"
buttonBg: "Фон кнопки" buttonBg: "Фон кнопки"

View File

@@ -1467,9 +1467,6 @@ _theme:
infoFg: "Chữ thông tin" infoFg: "Chữ thông tin"
infoWarnBg: "Nền cảnh báo" infoWarnBg: "Nền cảnh báo"
infoWarnFg: "Chữ cảnh báo" infoWarnFg: "Chữ cảnh báo"
cwBg: "Nền nút nội dung ẩn"
cwFg: "Chữ nút nội dung ẩn"
cwHoverBg: "Nền nút nội dung ẩn (Chạm)"
toastBg: "Nền thông báo" toastBg: "Nền thông báo"
toastFg: "Chữ thông báo" toastFg: "Chữ thông báo"
buttonBg: "Nền nút" buttonBg: "Nền nút"

View File

@@ -195,6 +195,7 @@ perHour: "每小时"
perDay: "每天" perDay: "每天"
stopActivityDelivery: "停止发送活动" stopActivityDelivery: "停止发送活动"
blockThisInstance: "阻止此服务器向本服务器推流" blockThisInstance: "阻止此服务器向本服务器推流"
silenceThisInstance: "使服务器静音"
operations: "操作" operations: "操作"
software: "软件" software: "软件"
version: "版本" version: "版本"
@@ -214,6 +215,8 @@ clearCachedFiles: "清除缓存"
clearCachedFilesConfirm: "确定要清除缓存文件?" clearCachedFilesConfirm: "确定要清除缓存文件?"
blockedInstances: "被封锁的服务器" blockedInstances: "被封锁的服务器"
blockedInstancesDescription: "设定要封锁的服务器,以换行来进行分割。被封锁的服务器将无法与本服务器进行交换通讯。子域名也同样会被封锁。" blockedInstancesDescription: "设定要封锁的服务器,以换行来进行分割。被封锁的服务器将无法与本服务器进行交换通讯。子域名也同样会被封锁。"
silencedInstances: "沉默的服务器"
silencedInstancesDescription: "设置要静音的服务器的主机,以换行符分隔。属于静默服务器的所有帐户都将被视为“静默”,所有关注都将成为请求,并且您将无法提及非关注者的本地帐户。被阻止的实例不受影响。"
muteAndBlock: "屏蔽/拉黑" muteAndBlock: "屏蔽/拉黑"
mutedUsers: "已屏蔽用户" mutedUsers: "已屏蔽用户"
blockedUsers: "已拉黑的用户" blockedUsers: "已拉黑的用户"
@@ -1673,9 +1676,6 @@ _theme:
infoFg: "信息文本" infoFg: "信息文本"
infoWarnBg: "警告背景" infoWarnBg: "警告背景"
infoWarnFg: "警告文本" infoWarnFg: "警告文本"
cwBg: "隐藏内容按钮背景"
cwFg: "隐藏内容按钮文本"
cwHoverBg: "隐藏内容按钮背景(悬停)"
toastBg: "Toast 通知背景" toastBg: "Toast 通知背景"
toastFg: "Toast 通知文本" toastFg: "Toast 通知文本"
buttonBg: "按钮背景" buttonBg: "按钮背景"
@@ -2130,3 +2130,6 @@ _moderationLogTypes:
createAd: "创建了广告" createAd: "创建了广告"
deleteAd: "删除了广告" deleteAd: "删除了广告"
updateAd: "更新了广告" updateAd: "更新了广告"
_fileViewer:
url: "URL"
uploadedAt: "添加日期"

View File

@@ -195,6 +195,7 @@ perHour: "每小時"
perDay: "每日" perDay: "每日"
stopActivityDelivery: "停止發送活動" stopActivityDelivery: "停止發送活動"
blockThisInstance: "封鎖此伺服器" blockThisInstance: "封鎖此伺服器"
silenceThisInstance: "禁言此伺服器"
operations: "操作" operations: "操作"
software: "軟體" software: "軟體"
version: "版本" version: "版本"
@@ -214,6 +215,8 @@ clearCachedFiles: "清除快取資料"
clearCachedFilesConfirm: "確定要清除所有遠端暫存資料嗎?" clearCachedFilesConfirm: "確定要清除所有遠端暫存資料嗎?"
blockedInstances: "已封鎖的伺服器" blockedInstances: "已封鎖的伺服器"
blockedInstancesDescription: "請逐行輸入需要封鎖的伺服器。已封鎖的伺服器將無法與本伺服器進行通訊。" blockedInstancesDescription: "請逐行輸入需要封鎖的伺服器。已封鎖的伺服器將無法與本伺服器進行通訊。"
silencedInstances: "被禁言的伺服器"
silencedInstancesDescription: "設定要禁言的伺服器主機名稱,以換行分隔。隸屬於禁言伺服器的所有帳戶都將被視為「禁言帳戶」,只能發出「追隨請求」,而且無法提及未追隨的本地帳戶。這不會影響已封鎖的實例。"
muteAndBlock: "靜音和封鎖" muteAndBlock: "靜音和封鎖"
mutedUsers: "被靜音的使用者" mutedUsers: "被靜音的使用者"
blockedUsers: "被封鎖的使用者" blockedUsers: "被封鎖的使用者"
@@ -531,6 +534,7 @@ serverLogs: "伺服器日誌"
deleteAll: "刪除所有記錄" deleteAll: "刪除所有記錄"
showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框" showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框"
showFixedPostFormInChannel: "於時間軸頁頂顯示「發送貼文」方框(頻道)" showFixedPostFormInChannel: "於時間軸頁頂顯示「發送貼文」方框(頻道)"
withRepliesByDefaultForNewlyFollowed: "在追隨其他人後,預設在時間軸納入回覆的貼文"
newNoteRecived: "發現新貼文" newNoteRecived: "發現新貼文"
sounds: "音效" sounds: "音效"
sound: "音效" sound: "音效"
@@ -1125,8 +1129,8 @@ showRenotes: "顯示轉發貼文"
edited: "已編輯" edited: "已編輯"
notificationRecieveConfig: "接受通知的設定" notificationRecieveConfig: "接受通知的設定"
mutualFollow: "互相追隨" mutualFollow: "互相追隨"
fileAttachedOnly: "包含附件" fileAttachedOnly: "顯示包含附件的貼文"
showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆" showRepliesToOthersInTimeline: "顯示給其他人的回覆"
hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆" hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
externalServices: "外部服務" externalServices: "外部服務"
impressum: "營運者資訊" impressum: "營運者資訊"
@@ -1685,9 +1689,6 @@ _theme:
infoFg: "資訊內容" infoFg: "資訊內容"
infoWarnBg: "警告背景" infoWarnBg: "警告背景"
infoWarnFg: "警告文字" infoWarnFg: "警告文字"
cwBg: "隱藏內容按鈕背景"
cwFg: "隱藏內容按鈕文字"
cwHoverBg: "隱藏內容按鈕背景(懸浮)"
toastBg: "通知背景" toastBg: "通知背景"
toastFg: "通知文本" toastFg: "通知文本"
buttonBg: "按鈕背景" buttonBg: "按鈕背景"
@@ -1924,6 +1925,7 @@ _exportOrImport:
userLists: "清單" userLists: "清單"
excludeMutingUsers: "排除被靜音的使用者" excludeMutingUsers: "排除被靜音的使用者"
excludeInactiveUsers: "排除不活躍帳戶" excludeInactiveUsers: "排除不活躍帳戶"
withReplies: "將被匯入的追隨中清單的貼文回覆包含在時間軸"
_charts: _charts:
federation: "聯邦宇宙" federation: "聯邦宇宙"
apRequest: "請求" apRequest: "請求"
@@ -2144,3 +2146,11 @@ _moderationLogTypes:
createAd: "建立廣告" createAd: "建立廣告"
deleteAd: "刪除廣告" deleteAd: "刪除廣告"
updateAd: "更新廣告" updateAd: "更新廣告"
_fileViewer:
title: "檔案詳細資訊"
type: "檔案類型 "
size: "檔案大小"
url: "URL"
uploadedAt: "加入日期"
attachedNotes: "含有附件的貼文"
thisPageCanBeSeenFromTheAuthor: "本頁面僅限上傳了這個檔案的使用者可以檢視。"

View File

@@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2023.10.0-beta.13", "version": "2023.10.2",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"packageManager": "pnpm@8.8.0", "packageManager": "pnpm@8.9.2",
"workspaces": [ "workspaces": [
"packages/frontend", "packages/frontend",
"packages/backend", "packages/backend",
@@ -47,14 +47,14 @@
"cssnano": "6.0.1", "cssnano": "6.0.1",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"postcss": "8.4.31", "postcss": "8.4.31",
"terser": "5.21.0", "terser": "5.22.0",
"typescript": "5.2.2" "typescript": "5.2.2"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "6.7.4", "@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.7.4", "@typescript-eslint/parser": "6.8.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "13.3.0", "cypress": "13.3.2",
"eslint": "8.51.0", "eslint": "8.51.0",
"start-server-and-test": "2.0.1" "start-server-and-test": "2.0.1"
}, },

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class InstanceSilence1697247230117 {
name = 'InstanceSilence1697247230117'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "silencedHosts" character varying(1024) array NOT NULL DEFAULT '{}'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`);
}
}

View File

@@ -0,0 +1,144 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class DeleteCreatedAt1697420555911 {
name = 'DeleteCreatedAt1697420555911'
async up(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_02878d441ceae15ce060b73daf"`);
await queryRunner.query(`DROP INDEX "public"."IDX_c8dfad3b72196dd1d6b5db168a"`);
await queryRunner.query(`DROP INDEX "public"."IDX_e11e649824a45d8ed01d597fd9"`);
await queryRunner.query(`DROP INDEX "public"."IDX_db2098070b2b5a523c58181f74"`);
await queryRunner.query(`DROP INDEX "public"."IDX_048a757923ed8b157e9895da53"`);
await queryRunner.query(`DROP INDEX "public"."IDX_1129c2ef687fc272df040bafaa"`);
await queryRunner.query(`DROP INDEX "public"."IDX_118ec703e596086fc4515acb39"`);
await queryRunner.query(`DROP INDEX "public"."IDX_b9a354f7941c1e779f3b33aea6"`);
await queryRunner.query(`DROP INDEX "public"."IDX_71cb7b435b7c0d4843317e7e16"`);
await queryRunner.query(`DROP INDEX "public"."IDX_11e71f2511589dcc8a4d3214f9"`);
await queryRunner.query(`DROP INDEX "public"."IDX_735a5544f9249d412255f47f95"`);
await queryRunner.query(`DROP INDEX "public"."IDX_582f8fab771a9040a12961f3e7"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8f1a239bd077c8864a20c62c2c"`);
await queryRunner.query(`DROP INDEX "public"."IDX_f86d57fbca33c7a4e6897490cc"`);
await queryRunner.query(`DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`);
await queryRunner.query(`DROP INDEX "public"."IDX_fbb4297c927a9b85e9cefa2eb1"`);
await queryRunner.query(`DROP INDEX "public"."IDX_0fb627e1c2f753262a74f0562d"`);
await queryRunner.query(`DROP INDEX "public"."IDX_149d2e44785707548c82999b01"`);
await queryRunner.query(`ALTER TABLE "drive_folder" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "app" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "announcement_read" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "user_list" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "auth_session" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "blocking" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "channel_following" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "channel_favorite" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "clip" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "clip_favorite" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "following" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "follow_request" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "gallery_post" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "gallery_like" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "moderation_log" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "muting" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "renote_muting" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "note_favorite" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "note_reaction" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "note_thread_muting" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "page_like" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "password_reset_request" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "poll_vote" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "promo_read" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "registration_ticket" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "signin" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "sw_subscription" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "user_list_favorite" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "user_list_membership" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "user_note_pining" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "user_pending" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "role_assignment" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "flash" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "flash_like" DROP COLUMN "createdAt"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "flash_like" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "flash" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "role_assignment" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "role" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "webhook" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "user_pending" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "user_note_pining" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "user_list_membership" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "user_list_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "sw_subscription" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "signin" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "registry_item" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "registration_ticket" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "promo_read" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "poll_vote" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "password_reset_request" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "page_like" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "page" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_thread_muting" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_reaction" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "renote_muting" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "muting" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "moderation_log" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "gallery_like" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "gallery_post" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "follow_request" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "following" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "clip_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "note" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "clip" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "channel_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "channel_following" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "channel" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "blocking" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "auth_session" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "antenna" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "user_list" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "announcement_read" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "announcement" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "ad" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "access_token" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "app" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "user" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "drive_file" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`ALTER TABLE "drive_folder" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`CREATE INDEX "IDX_149d2e44785707548c82999b01" ON "flash" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_0fb627e1c2f753262a74f0562d" ON "poll_vote" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_fbb4297c927a9b85e9cefa2eb1" ON "page" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_f86d57fbca33c7a4e6897490cc" ON "muting" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_582f8fab771a9040a12961f3e7" ON "following" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_735a5544f9249d412255f47f95" ON "channel_favorite" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_11e71f2511589dcc8a4d3214f9" ON "channel_following" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_71cb7b435b7c0d4843317e7e16" ON "channel" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_b9a354f7941c1e779f3b33aea6" ON "blocking" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_118ec703e596086fc4515acb39" ON "announcement" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_1129c2ef687fc272df040bafaa" ON "ad" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_048a757923ed8b157e9895da53" ON "app" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_db2098070b2b5a523c58181f74" ON "abuse_user_report" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_e11e649824a45d8ed01d597fd9" ON "user" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_c8dfad3b72196dd1d6b5db168a" ON "drive_file" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_02878d441ceae15ce060b73daf" ON "drive_folder" ("createdAt") `);
}
}

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class AntennaLocalOnly1697436246389 {
name = 'AntennaLocalOnly1697436246389'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "antenna" ADD "localOnly" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "localOnly"`);
}
}

View File

@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class FollowRequestWithReplies1697441463087 {
name = 'FollowRequestWithReplies1697441463087'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "follow_request" ADD "withReplies" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "follow_request" DROP COLUMN "withReplies"`);
}
}

View File

@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class NoteReactionAndUserPairCache1697673894459 {
name = 'NoteReactionAndUserPairCache1697673894459'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "note" ADD "reactionAndUserPairCache" character varying(1024) array NOT NULL DEFAULT '{}'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "reactionAndUserPairCache"`);
}
}

View File

@@ -59,9 +59,9 @@
"@aws-sdk/client-s3": "3.412.0", "@aws-sdk/client-s3": "3.412.0",
"@aws-sdk/lib-storage": "3.412.0", "@aws-sdk/lib-storage": "3.412.0",
"@smithy/node-http-handler": "2.1.5", "@smithy/node-http-handler": "2.1.5",
"@bull-board/api": "5.8.4", "@bull-board/api": "5.9.1",
"@bull-board/fastify": "5.8.4", "@bull-board/fastify": "5.9.1",
"@bull-board/ui": "5.8.4", "@bull-board/ui": "5.9.1",
"@discordapp/twemoji": "14.1.2", "@discordapp/twemoji": "14.1.2",
"@fastify/accepts": "4.2.0", "@fastify/accepts": "4.2.0",
"@fastify/cookie": "9.1.0", "@fastify/cookie": "9.1.0",
@@ -75,10 +75,10 @@
"@nestjs/core": "10.2.7", "@nestjs/core": "10.2.7",
"@nestjs/testing": "10.2.7", "@nestjs/testing": "10.2.7",
"@peertube/http-signature": "1.7.0", "@peertube/http-signature": "1.7.0",
"@simplewebauthn/server": "8.2.0", "@simplewebauthn/server": "8.3.2",
"@sinonjs/fake-timers": "11.1.0", "@sinonjs/fake-timers": "11.2.1",
"@swc/cli": "0.1.62", "@swc/cli": "0.1.62",
"@swc/core": "1.3.92", "@swc/core": "1.3.93",
"accepts": "1.3.8", "accepts": "1.3.8",
"ajv": "8.12.0", "ajv": "8.12.0",
"archiver": "6.0.1", "archiver": "6.0.1",
@@ -86,7 +86,7 @@
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"body-parser": "1.20.2", "body-parser": "1.20.2",
"bullmq": "4.12.2", "bullmq": "4.12.5",
"cacheable-lookup": "7.0.0", "cacheable-lookup": "7.0.0",
"cbor": "9.0.1", "cbor": "9.0.1",
"chalk": "5.3.0", "chalk": "5.3.0",
@@ -97,7 +97,7 @@
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"fastify": "4.23.2", "fastify": "4.24.3",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "18.5.0", "file-type": "18.5.0",
"fluent-ffmpeg": "2.1.2", "fluent-ffmpeg": "2.1.2",
@@ -121,13 +121,13 @@
"mime-types": "2.1.35", "mime-types": "2.1.35",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"ms": "3.0.0-canary.1", "ms": "3.0.0-canary.1",
"nanoid": "5.0.1", "nanoid": "5.0.2",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.3.2", "node-fetch": "3.3.2",
"nodemailer": "6.9.6", "nodemailer": "6.9.6",
"nsfwjs": "2.4.2", "nsfwjs": "2.4.2",
"oauth": "0.10.0", "oauth": "0.10.0",
"oauth2orize": "1.11.1", "oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2", "oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"otpauth": "9.1.5", "otpauth": "9.1.5",
@@ -155,7 +155,7 @@
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"summaly": "github:misskey-dev/summaly", "summaly": "github:misskey-dev/summaly",
"systeminformation": "5.21.11", "systeminformation": "5.21.12",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tmp": "0.2.1", "tmp": "0.2.1",
"tsc-alias": "1.8.8", "tsc-alias": "1.8.8",
@@ -173,47 +173,47 @@
"@jest/globals": "29.7.0", "@jest/globals": "29.7.0",
"@simplewebauthn/typescript-types": "8.0.0", "@simplewebauthn/typescript-types": "8.0.0",
"@swc/jest": "0.2.29", "@swc/jest": "0.2.29",
"@types/accepts": "1.3.5", "@types/accepts": "1.3.6",
"@types/archiver": "5.3.3", "@types/archiver": "5.3.4",
"@types/bcryptjs": "2.4.4", "@types/bcryptjs": "2.4.5",
"@types/body-parser": "1.19.3", "@types/body-parser": "1.19.4",
"@types/cbor": "6.0.0", "@types/cbor": "6.0.0",
"@types/color-convert": "2.0.1", "@types/color-convert": "2.0.2",
"@types/content-disposition": "0.5.6", "@types/content-disposition": "0.5.7",
"@types/fluent-ffmpeg": "2.1.22", "@types/fluent-ffmpeg": "2.1.23",
"@types/http-link-header": "1.0.3", "@types/http-link-header": "1.0.4",
"@types/jest": "29.5.5", "@types/jest": "29.5.6",
"@types/js-yaml": "4.0.6", "@types/js-yaml": "4.0.8",
"@types/jsdom": "21.1.3", "@types/jsdom": "21.1.4",
"@types/jsonld": "1.5.10", "@types/jsonld": "1.5.11",
"@types/jsrsasign": "10.5.9", "@types/jsrsasign": "10.5.11",
"@types/mime-types": "2.1.2", "@types/mime-types": "2.1.3",
"@types/ms": "0.7.32", "@types/ms": "0.7.33",
"@types/node": "20.8.3", "@types/node": "20.8.7",
"@types/node-fetch": "3.0.3", "@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.11", "@types/nodemailer": "6.4.13",
"@types/oauth": "0.9.2", "@types/oauth": "0.9.3",
"@types/oauth2orize": "1.11.1", "@types/oauth2orize": "1.11.2",
"@types/oauth2orize-pkce": "0.1.0", "@types/oauth2orize-pkce": "0.1.1",
"@types/pg": "8.10.3", "@types/pg": "8.10.7",
"@types/pug": "2.0.7", "@types/pug": "2.0.8",
"@types/punycode": "2.1.0", "@types/punycode": "2.1.1",
"@types/qrcode": "1.5.2", "@types/qrcode": "1.5.4",
"@types/random-seed": "0.3.3", "@types/random-seed": "0.3.4",
"@types/ratelimiter": "3.4.4", "@types/ratelimiter": "3.4.5",
"@types/rename": "1.0.5", "@types/rename": "1.0.6",
"@types/sanitize-html": "2.9.1", "@types/sanitize-html": "2.9.3",
"@types/semver": "7.5.3", "@types/semver": "7.5.4",
"@types/sharp": "0.32.0", "@types/sharp": "0.32.0",
"@types/simple-oauth2": "5.0.5", "@types/simple-oauth2": "5.0.6",
"@types/sinonjs__fake-timers": "8.1.3", "@types/sinonjs__fake-timers": "8.1.4",
"@types/tinycolor2": "1.4.4", "@types/tinycolor2": "1.4.5",
"@types/tmp": "0.2.4", "@types/tmp": "0.2.5",
"@types/vary": "1.1.1", "@types/vary": "1.1.2",
"@types/web-push": "3.6.1", "@types/web-push": "3.6.2",
"@types/ws": "8.5.6", "@types/ws": "8.5.8",
"@typescript-eslint/eslint-plugin": "6.7.4", "@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.7.4", "@typescript-eslint/parser": "6.8.0",
"aws-sdk-client-mock": "3.0.0", "aws-sdk-client-mock": "3.0.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "8.51.0", "eslint": "8.51.0",

View File

@@ -180,13 +180,13 @@ export class AccountMoveService {
{ muteeId: dst.id, expiresAt: IsNull() }, { muteeId: dst.id, expiresAt: IsNull() },
).then(mutings => mutings.map(muting => muting.muterId)); ).then(mutings => mutings.map(muting => muting.muterId));
const newMutings: Map<string, { muterId: string; muteeId: string; createdAt: Date; expiresAt: Date | null; }> = new Map(); const newMutings: Map<string, { muterId: string; muteeId: string; expiresAt: Date | null; }> = new Map();
// 重複しないようにIDを生成 // 重複しないようにIDを生成
const genId = (): string => { const genId = (): string => {
let id: string; let id: string;
do { do {
id = this.idService.genId(); id = this.idService.gen();
} while (newMutings.has(id)); } while (newMutings.has(id));
return id; return id;
}; };
@@ -194,7 +194,6 @@ export class AccountMoveService {
if (existingMutingsMuterUserIds.includes(muting.muterId)) continue; // skip if already muted indefinitely if (existingMutingsMuterUserIds.includes(muting.muterId)) continue; // skip if already muted indefinitely
newMutings.set(genId(), { newMutings.set(genId(), {
...muting, ...muting,
createdAt: new Date(),
muteeId: dst.id, muteeId: dst.id,
}); });
} }
@@ -228,20 +227,19 @@ export class AccountMoveService {
}, },
}).then(memberships => memberships.map(membership => membership.userListId)); }).then(memberships => memberships.map(membership => membership.userListId));
const newMemberships: Map<string, { createdAt: Date; userId: string; userListId: string; userListUserId: string; }> = new Map(); const newMemberships: Map<string, { userId: string; userListId: string; userListUserId: string; }> = new Map();
// 重複しないようにIDを生成 // 重複しないようにIDを生成
const genId = (): string => { const genId = (): string => {
let id: string; let id: string;
do { do {
id = this.idService.genId(); id = this.idService.gen();
} while (newMemberships.has(id)); } while (newMemberships.has(id));
return id; return id;
}; };
for (const membership of oldMemberships) { for (const membership of oldMemberships) {
if (existingUserListIds.includes(membership.userListId)) continue; // skip if dst exists in this user's list if (existingUserListIds.includes(membership.userListId)) continue; // skip if dst exists in this user's list
newMemberships.set(genId(), { newMemberships.set(genId(), {
createdAt: new Date(),
userId: dst.id, userId: dst.id,
userListId: membership.userListId, userListId: membership.userListId,
userListUserId: membership.userListUserId, userListUserId: membership.userListUserId,

View File

@@ -53,7 +53,7 @@ export class AnnouncementService {
})) }))
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
qb.orWhere('announcement.forExistingUsers = false'); qb.orWhere('announcement.forExistingUsers = false');
qb.orWhere('announcement.createdAt > :createdAt', { createdAt: user.createdAt }); qb.orWhere('announcement.id > :userId', { userId: user.id });
})) }))
.andWhere(`announcement.id NOT IN (${ readsQuery.getQuery() })`); .andWhere(`announcement.id NOT IN (${ readsQuery.getQuery() })`);
@@ -65,8 +65,7 @@ export class AnnouncementService {
@bindThis @bindThis
public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
const announcement = await this.announcementsRepository.insert({ const announcement = await this.announcementsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
updatedAt: null, updatedAt: null,
title: values.title, title: values.title,
text: values.text, text: values.text,
@@ -179,8 +178,7 @@ export class AnnouncementService {
public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> { public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
try { try {
await this.announcementReadsRepository.insert({ await this.announcementReadsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
announcementId: announcementId, announcementId: announcementId,
userId: user.id, userId: user.id,
}); });
@@ -204,7 +202,7 @@ export class AnnouncementService {
const reads = me ? (options?.reads ?? await this.getReads(me.id)) : []; const reads = me ? (options?.reads ?? await this.getReads(me.id)) : [];
return announcements.map(announcement => ({ return announcements.map(announcement => ({
id: announcement.id, id: announcement.id,
createdAt: announcement.createdAt.toISOString(), createdAt: this.idService.parse(announcement.id).date.toISOString(),
updatedAt: announcement.updatedAt?.toISOString() ?? null, updatedAt: announcement.updatedAt?.toISOString() ?? null,
text: announcement.text, text: announcement.text,
title: announcement.title, title: announcement.title,

View File

@@ -16,7 +16,7 @@ import type { AntennasRepository, UserListMembershipsRepository } from '@/models
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js';
import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import type { OnApplicationShutdown } from '@nestjs/common'; import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
@@ -39,7 +39,7 @@ export class AntennaService implements OnApplicationShutdown {
private utilityService: UtilityService, private utilityService: UtilityService,
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
private redisTimelineService: RedisTimelineService, private funoutTimelineService: FunoutTimelineService,
) { ) {
this.antennasFetched = false; this.antennasFetched = false;
this.antennas = []; this.antennas = [];
@@ -57,14 +57,12 @@ export class AntennaService implements OnApplicationShutdown {
case 'antennaCreated': case 'antennaCreated':
this.antennas.push({ this.antennas.push({
...body, ...body,
createdAt: new Date(body.createdAt),
lastUsedAt: new Date(body.lastUsedAt), lastUsedAt: new Date(body.lastUsedAt),
}); });
break; break;
case 'antennaUpdated': case 'antennaUpdated':
this.antennas[this.antennas.findIndex(a => a.id === body.id)] = { this.antennas[this.antennas.findIndex(a => a.id === body.id)] = {
...body, ...body,
createdAt: new Date(body.createdAt),
lastUsedAt: new Date(body.lastUsedAt), lastUsedAt: new Date(body.lastUsedAt),
}; };
break; break;
@@ -86,7 +84,7 @@ export class AntennaService implements OnApplicationShutdown {
const redisPipeline = this.redisForTimelines.pipeline(); const redisPipeline = this.redisForTimelines.pipeline();
for (const antenna of matchedAntennas) { for (const antenna of matchedAntennas) {
this.redisTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline); this.funoutTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline);
this.globalEventService.publishAntennaStream(antenna.id, 'note', note); this.globalEventService.publishAntennaStream(antenna.id, 'note', note);
} }
@@ -100,6 +98,8 @@ export class AntennaService implements OnApplicationShutdown {
if (note.visibility === 'specified') return false; if (note.visibility === 'specified') return false;
if (note.visibility === 'followers') return false; if (note.visibility === 'followers') return false;
if (antenna.localOnly && noteUser.host != null) return false;
if (!antenna.withReplies && note.replyId != null) return false; if (!antenna.withReplies && note.replyId != null) return false;
if (antenna.src === 'home') { if (antenna.src === 'home') {

View File

@@ -46,8 +46,7 @@ export class ClipService {
} }
const clip = await this.clipsRepository.insert({ const clip = await this.clipsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
userId: me.id, userId: me.id,
name: name, name: name,
isPublic: isPublic, isPublic: isPublic,
@@ -109,7 +108,7 @@ export class ClipService {
try { try {
await this.clipNotesRepository.insert({ await this.clipNotesRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
noteId: noteId, noteId: noteId,
clipId: clip.id, clipId: clip.id,
}); });

View File

@@ -61,7 +61,7 @@ import { FileInfoService } from './FileInfoService.js';
import { SearchService } from './SearchService.js'; import { SearchService } from './SearchService.js';
import { ClipService } from './ClipService.js'; import { ClipService } from './ClipService.js';
import { FeaturedService } from './FeaturedService.js'; import { FeaturedService } from './FeaturedService.js';
import { RedisTimelineService } from './RedisTimelineService.js'; import { FunoutTimelineService } from './FunoutTimelineService.js';
import { ChartLoggerService } from './chart/ChartLoggerService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.js';
import FederationChart from './chart/charts/federation.js'; import FederationChart from './chart/charts/federation.js';
import NotesChart from './chart/charts/notes.js'; import NotesChart from './chart/charts/notes.js';
@@ -190,7 +190,7 @@ const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: Fi
const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService }; const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService }; const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService }; const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
const $RedisTimelineService: Provider = { provide: 'RedisTimelineService', useExisting: RedisTimelineService }; const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService };
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
@@ -323,7 +323,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SearchService, SearchService,
ClipService, ClipService,
FeaturedService, FeaturedService,
RedisTimelineService, FunoutTimelineService,
ChartLoggerService, ChartLoggerService,
FederationChart, FederationChart,
NotesChart, NotesChart,
@@ -449,7 +449,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SearchService, $SearchService,
$ClipService, $ClipService,
$FeaturedService, $FeaturedService,
$RedisTimelineService, $FunoutTimelineService,
$ChartLoggerService, $ChartLoggerService,
$FederationChart, $FederationChart,
$NotesChart, $NotesChart,
@@ -576,7 +576,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SearchService, SearchService,
ClipService, ClipService,
FeaturedService, FeaturedService,
RedisTimelineService, FunoutTimelineService,
FederationChart, FederationChart,
NotesChart, NotesChart,
UsersChart, UsersChart,
@@ -701,7 +701,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SearchService, $SearchService,
$ClipService, $ClipService,
$FeaturedService, $FeaturedService,
$RedisTimelineService, $FunoutTimelineService,
$FederationChart, $FederationChart,
$NotesChart, $NotesChart,
$UsersChart, $UsersChart,

View File

@@ -52,8 +52,7 @@ export class CreateSystemUserService {
if (exist) throw new Error('the user is already exists'); if (exist) throw new Error('the user is already exists');
account = await transactionalEntityManager.insert(MiUser, { account = await transactionalEntityManager.insert(MiUser, {
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
username: username, username: username,
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: null, host: null,

View File

@@ -69,7 +69,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
}, moderator?: MiUser): Promise<MiEmoji> { }, moderator?: MiUser): Promise<MiEmoji> {
const emoji = await this.emojisRepository.insert({ const emoji = await this.emojisRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
updatedAt: new Date(), updatedAt: new Date(),
name: data.name, name: data.name,
category: data.category, category: data.category,
@@ -331,7 +331,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
const queryOrNull = async () => (await this.emojisRepository.findOneBy({ const queryOrNull = async () => (await this.emojisRepository.findOneBy({
name, name,
host: host ?? IsNull(), host,
})) ?? null; })) ?? null;
const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull); const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull);

View File

@@ -564,8 +564,7 @@ export class DriveService {
const folder = await fetchFolder(); const folder = await fetchFolder();
let file = new MiDriveFile(); let file = new MiDriveFile();
file.id = this.idService.genId(); file.id = this.idService.gen();
file.createdAt = new Date();
file.userId = user ? user.id : null; file.userId = user ? user.id : null;
file.userHost = user ? user.host : null; file.userHost = user ? user.host : null;
file.folderId = folder !== null ? folder.id : null; file.folderId = folder !== null ? folder.id : null;

View File

@@ -56,7 +56,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
if (index == null) { if (index == null) {
const i = await this.instancesRepository.insert({ const i = await this.instancesRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
host, host,
firstRetrievedAt: new Date(), firstRetrievedAt: new Date(),
}).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0]));

View File

@@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@Injectable() @Injectable()
export class RedisTimelineService { export class FunoutTimelineService {
constructor( constructor(
@Inject(DI.redisForTimelines) @Inject(DI.redisForTimelines)
private redisForTimelines: Redis.Redis, private redisForTimelines: Redis.Redis,
@@ -44,13 +44,13 @@ export class RedisTimelineService {
public get(name: string, untilId?: string | null, sinceId?: string | null) { public get(name: string, untilId?: string | null, sinceId?: string | null) {
if (untilId && sinceId) { if (untilId && sinceId) {
return this.redisForTimelines.lrange('list:' + name, 0, -1) return this.redisForTimelines.lrange('list:' + name, 0, -1)
.then(ids => ids.filter(id => id > untilId && id < sinceId).sort((a, b) => a > b ? -1 : 1)); .then(ids => ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1));
} else if (untilId) { } else if (untilId) {
return this.redisForTimelines.lrange('list:' + name, 0, -1) return this.redisForTimelines.lrange('list:' + name, 0, -1)
.then(ids => ids.filter(id => id > untilId).sort((a, b) => a > b ? -1 : 1)); .then(ids => ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1));
} else if (sinceId) { } else if (sinceId) {
return this.redisForTimelines.lrange('list:' + name, 0, -1) return this.redisForTimelines.lrange('list:' + name, 0, -1)
.then(ids => ids.filter(id => id < sinceId).sort((a, b) => a < b ? -1 : 1)); .then(ids => ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1));
} else { } else {
return this.redisForTimelines.lrange('list:' + name, 0, -1) return this.redisForTimelines.lrange('list:' + name, 0, -1)
.then(ids => ids.sort((a, b) => a > b ? -1 : 1)); .then(ids => ids.sort((a, b) => a > b ? -1 : 1));
@@ -68,13 +68,18 @@ export class RedisTimelineService {
const tls = res.map(r => r[1] as string[]); const tls = res.map(r => r[1] as string[]);
return tls.map(ids => return tls.map(ids =>
(untilId && sinceId) (untilId && sinceId)
? ids.filter(id => id > untilId && id < sinceId).sort((a, b) => a > b ? -1 : 1) ? ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1)
: untilId : untilId
? ids.filter(id => id > untilId).sort((a, b) => a > b ? -1 : 1) ? ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1)
: sinceId : sinceId
? ids.filter(id => id < sinceId).sort((a, b) => a < b ? -1 : 1) ? ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1)
: ids.sort((a, b) => a > b ? -1 : 1), : ids.sort((a, b) => a > b ? -1 : 1),
); );
}); });
} }
@bindThis
public purge(name: string) {
return this.redisForTimelines.del('list:' + name);
}
} }

View File

@@ -45,7 +45,7 @@ export class HashtagService {
await this.updateHashtag(user, tag, true, true); await this.updateHashtag(user, tag, true, true);
} }
for (const tag of (user.tags ?? []).filter(x => !tags.includes(x))) { for (const tag of user.tags.filter(x => !tags.includes(x))) {
await this.updateHashtag(user, tag, true, false); await this.updateHashtag(user, tag, true, false);
} }
} }
@@ -120,7 +120,7 @@ export class HashtagService {
} else { } else {
if (isUserAttached) { if (isUserAttached) {
this.hashtagsRepository.insert({ this.hashtagsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
name: tag, name: tag,
mentionedUserIds: [], mentionedUserIds: [],
mentionedUsersCount: 0, mentionedUsersCount: 0,
@@ -137,7 +137,7 @@ export class HashtagService {
} as MiHashtag); } as MiHashtag);
} else { } else {
this.hashtagsRepository.insert({ this.hashtagsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
name: tag, name: tag,
mentionedUserIds: [user.id], mentionedUserIds: [user.id],
mentionedUsersCount: 1, mentionedUsersCount: 1,

View File

@@ -26,17 +26,21 @@ export class IdService {
this.method = config.id.toLowerCase(); this.method = config.id.toLowerCase();
} }
/**
* 時間を元にIDを生成します(省略時は現在日時)
* @param time 日時
*/
@bindThis @bindThis
public genId(date?: Date): string { public gen(time?: number): string {
if (!date || (date > new Date())) date = new Date(); const t = (!time || (time > Date.now())) ? Date.now() : time;
switch (this.method) { switch (this.method) {
case 'aid': return genAid(date); case 'aid': return genAid(t);
case 'aidx': return genAidx(date); case 'aidx': return genAidx(t);
case 'meid': return genMeid(date); case 'meid': return genMeid(t);
case 'meidg': return genMeidg(date); case 'meidg': return genMeidg(t);
case 'ulid': return ulid(date.getTime()); case 'ulid': return ulid(t);
case 'objectid': return genObjectId(date); case 'objectid': return genObjectId(t);
default: throw new Error('unrecognized id generation method'); default: throw new Error('unrecognized id generation method');
} }
} }

View File

@@ -24,8 +24,7 @@ export class ModerationLogService {
@bindThis @bindThis
public async log<T extends typeof moderationLogTypes[number]>(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) { public async log<T extends typeof moderationLogTypes[number]>(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) {
await this.moderationLogsRepository.insert({ await this.moderationLogsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
userId: moderator.id, userId: moderator.id,
type: type, type: type,
info: (info as any) ?? {}, info: (info as any) ?? {},

View File

@@ -54,7 +54,8 @@ import { RoleService } from '@/core/RoleService.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { SearchService } from '@/core/SearchService.js'; import { SearchService } from '@/core/SearchService.js';
import { FeaturedService } from '@/core/FeaturedService.js'; import { FeaturedService } from '@/core/FeaturedService.js';
import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import { UtilityService } from '@/core/UtilityService.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@@ -195,7 +196,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private idService: IdService, private idService: IdService,
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
private queueService: QueueService, private queueService: QueueService,
private redisTimelineService: RedisTimelineService, private funoutTimelineService: FunoutTimelineService,
private noteReadService: NoteReadService, private noteReadService: NoteReadService,
private notificationService: NotificationService, private notificationService: NotificationService,
private relayService: RelayService, private relayService: RelayService,
@@ -214,6 +215,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private perUserNotesChart: PerUserNotesChart, private perUserNotesChart: PerUserNotesChart,
private activeUsersChart: ActiveUsersChart, private activeUsersChart: ActiveUsersChart,
private instanceChart: InstanceChart, private instanceChart: InstanceChart,
private utilityService: UtilityService,
) { } ) { }
@bindThis @bindThis
@@ -221,8 +223,8 @@ export class NoteCreateService implements OnApplicationShutdown {
id: MiUser['id']; id: MiUser['id'];
username: MiUser['username']; username: MiUser['username'];
host: MiUser['host']; host: MiUser['host'];
createdAt: MiUser['createdAt'];
isBot: MiUser['isBot']; isBot: MiUser['isBot'];
isCat: MiUser['isCat'];
}, data: Option, silent = false): Promise<MiNote> { }, data: Option, silent = false): Promise<MiNote> {
// チャンネル外にリプライしたら対象のスコープに合わせる // チャンネル外にリプライしたら対象のスコープに合わせる
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
@@ -247,8 +249,10 @@ export class NoteCreateService implements OnApplicationShutdown {
if (data.channel != null) data.visibleUsers = []; if (data.channel != null) data.visibleUsers = [];
if (data.channel != null) data.localOnly = true; if (data.channel != null) data.localOnly = true;
const meta = await this.metaService.fetch();
if (data.visibility === 'public' && data.channel == null) { if (data.visibility === 'public' && data.channel == null) {
const sensitiveWords = (await this.metaService.fetch()).sensitiveWords; const sensitiveWords = meta.sensitiveWords;
if (this.isSensitive(data, sensitiveWords)) { if (this.isSensitive(data, sensitiveWords)) {
data.visibility = 'home'; data.visibility = 'home';
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { } else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
@@ -256,6 +260,12 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
} }
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
if (data.visibility === 'public' && inSilencedInstance && user.host !== null) {
data.visibility = 'home';
}
if (data.renote) { if (data.renote) {
switch (data.renote.visibility) { switch (data.renote.visibility) {
case 'public': case 'public':
@@ -312,7 +322,7 @@ export class NoteCreateService implements OnApplicationShutdown {
// Parse MFM if needed // Parse MFM if needed
if (!tags || !emojis || !mentionedUsers) { if (!tags || !emojis || !mentionedUsers) {
const tokens = data.text ? mfm.parse(data.text)! : []; const tokens = (data.text ? mfm.parse(data.text)! : []);
const cwTokens = data.cw ? mfm.parse(data.cw)! : []; const cwTokens = data.cw ? mfm.parse(data.cw)! : [];
const choiceTokens = data.poll && data.poll.choices const choiceTokens = data.poll && data.poll.choices
? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) ? concat(data.poll.choices.map(choice => mfm.parse(choice)!))
@@ -327,7 +337,7 @@ export class NoteCreateService implements OnApplicationShutdown {
mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens); mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens);
} }
tags = tags.filter(tag => Array.from(tag ?? '').length <= 128).splice(0, 32); tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
mentionedUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId })); mentionedUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId }));
@@ -360,8 +370,7 @@ export class NoteCreateService implements OnApplicationShutdown {
@bindThis @bindThis
private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
const insert = new MiNote({ const insert = new MiNote({
id: this.idService.genId(data.createdAt!), id: this.idService.gen(data.createdAt?.getTime()),
createdAt: data.createdAt!,
fileIds: data.files ? data.files.map(file => file.id) : [], fileIds: data.files ? data.files.map(file => file.id) : [],
replyId: data.reply ? data.reply.id : null, replyId: data.reply ? data.reply.id : null,
renoteId: data.renote ? data.renote.id : null, renoteId: data.renote ? data.renote.id : null,
@@ -460,7 +469,6 @@ export class NoteCreateService implements OnApplicationShutdown {
id: MiUser['id']; id: MiUser['id'];
username: MiUser['username']; username: MiUser['username'];
host: MiUser['host']; host: MiUser['host'];
createdAt: MiUser['createdAt'];
isBot: MiUser['isBot']; isBot: MiUser['isBot'];
}, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
@@ -554,7 +562,7 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
// Pack the note // Pack the note
const noteObj = await this.noteEntityService.pack(note); const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true, withReactionAndUserPairCache: true });
this.globalEventService.publishNotesStream(noteObj); this.globalEventService.publishNotesStream(noteObj);
@@ -821,9 +829,9 @@ export class NoteCreateService implements OnApplicationShutdown {
const r = this.redisForTimelines.pipeline(); const r = this.redisForTimelines.pipeline();
if (note.channelId) { if (note.channelId) {
this.redisTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r); this.funoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r);
this.redisTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); this.funoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
const channelFollowings = await this.channelFollowingsRepository.find({ const channelFollowings = await this.channelFollowingsRepository.find({
where: { where: {
@@ -833,9 +841,9 @@ export class NoteCreateService implements OnApplicationShutdown {
}); });
for (const channelFollowing of channelFollowings) { for (const channelFollowing of channelFollowings) {
this.redisTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); this.funoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
this.redisTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); this.funoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
} }
} }
} else { } else {
@@ -868,14 +876,14 @@ export class NoteCreateService implements OnApplicationShutdown {
// 基本的にvisibleUserIdsには自身のidが含まれている前提であること // 基本的にvisibleUserIdsには自身のidが含まれている前提であること
if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue; if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
// 自分自身以外への返信 // 自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
if (note.replyId && note.replyUserId !== note.userId) { if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
if (!following.withReplies) continue; if (!following.withReplies) continue;
} }
this.redisTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); this.funoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
this.redisTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); this.funoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
} }
} }
@@ -886,37 +894,41 @@ export class NoteCreateService implements OnApplicationShutdown {
!note.visibleUserIds.some(v => v === userListMembership.userListUserId) !note.visibleUserIds.some(v => v === userListMembership.userListUserId)
) continue; ) continue;
// 自分自身以外への返信 // 自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合
if (note.replyId && note.replyUserId !== note.userId) { if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) {
if (!userListMembership.withReplies) continue; if (!userListMembership.withReplies) continue;
} }
this.redisTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r); this.funoutTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
this.redisTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r); this.funoutTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r);
} }
} }
if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL
this.redisTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r); this.funoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
this.redisTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); this.funoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
} }
} }
// 自分自身以外への返信 // 自分自身以外への返信
if (note.replyId && note.replyUserId !== note.userId) { if (note.replyId && note.replyUserId !== note.userId) {
this.redisTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); this.funoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
if (note.visibility === 'public' && note.userHost == null) {
this.funoutTimelineService.push('localTimelineWithReplies', note.id, 300, r);
}
} else { } else {
this.redisTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); this.funoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
this.redisTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r); this.funoutTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r);
} }
if (note.visibility === 'public' && note.userHost == null) { if (note.visibility === 'public' && note.userHost == null) {
this.redisTimelineService.push('localTimeline', note.id, 1000, r); this.funoutTimelineService.push('localTimeline', note.id, 1000, r);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
this.redisTimelineService.push('localTimelineWithFiles', note.id, 500, r); this.funoutTimelineService.push('localTimelineWithFiles', note.id, 500, r);
} }
} }
} }

View File

@@ -71,8 +71,7 @@ export class NotePiningService {
} }
await this.userNotePiningsRepository.insert({ await this.userNotePiningsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
userId: user.id, userId: user.id,
noteId: note.id, noteId: note.id,
} as MiUserNotePining); } as MiUserNotePining);

View File

@@ -57,7 +57,7 @@ export class NoteReadService implements OnApplicationShutdown {
if (isThreadMuted) return; if (isThreadMuted) return;
const unread = { const unread = {
id: this.idService.genId(), id: this.idService.gen(),
noteId: note.id, noteId: note.id,
userId: userId, userId: userId,
isSpecified: params.isSpecified, isSpecified: params.isSpecified,

View File

@@ -125,7 +125,7 @@ export class NotificationService implements OnApplicationShutdown {
} }
const notification = { const notification = {
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(), createdAt: new Date(),
type: type, type: type,
notifierId: notifierId, notifierId: notifierId,

View File

@@ -72,10 +72,8 @@ export class PollService {
throw new Error('already voted'); throw new Error('already voted');
} }
// Create vote
await this.pollVotesRepository.insert({ await this.pollVotesRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
noteId: note.id, noteId: note.id,
userId: user.id, userId: user.id,
choice: choice, choice: choice,

View File

@@ -52,14 +52,14 @@ export class QueryService {
q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId });
q.orderBy(`${q.alias}.id`, 'DESC'); q.orderBy(`${q.alias}.id`, 'DESC');
} else if (sinceDate && untilDate) { } else if (sinceDate && untilDate) {
q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.gen(sinceDate) });
q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.gen(untilDate) });
q.orderBy(`${q.alias}.id`, 'DESC'); q.orderBy(`${q.alias}.id`, 'DESC');
} else if (sinceDate) { } else if (sinceDate) {
q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.gen(sinceDate) });
q.orderBy(`${q.alias}.id`, 'ASC'); q.orderBy(`${q.alias}.id`, 'ASC');
} else if (untilDate) { } else if (untilDate) {
q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.gen(untilDate) });
q.orderBy(`${q.alias}.id`, 'DESC'); q.orderBy(`${q.alias}.id`, 'DESC');
} else { } else {
q.orderBy(`${q.alias}.id`, 'DESC'); q.orderBy(`${q.alias}.id`, 'DESC');

View File

@@ -237,10 +237,11 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id']) { public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id'], withReplies?: boolean) {
return this.dbQueue.add('importFollowing', { return this.dbQueue.add('importFollowing', {
user: { id: user.id }, user: { id: user.id },
fileId: fileId, fileId: fileId,
withReplies,
}, { }, {
removeOnComplete: true, removeOnComplete: true,
removeOnFail: true, removeOnFail: true,
@@ -248,8 +249,8 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportFollowingToDbJob(user: ThinUser, targets: string[]) { public createImportFollowingToDbJob(user: ThinUser, targets: string[], withReplies?: boolean) {
const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel })); const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel, withReplies }));
return this.dbQueue.addBulk(jobs); return this.dbQueue.addBulk(jobs);
} }
@@ -342,7 +343,7 @@ export class QueueService {
} }
@bindThis @bindThis
public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean }[]) { public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean, withReplies?: boolean }[]) {
const jobs = followings.map(rel => this.generateRelationshipJobData('follow', rel)); const jobs = followings.map(rel => this.generateRelationshipJobData('follow', rel));
return this.relationshipQueue.addBulk(jobs); return this.relationshipQueue.addBulk(jobs);
} }
@@ -384,6 +385,7 @@ export class QueueService {
to: { id: data.to.id }, to: { id: data.to.id },
silent: data.silent, silent: data.silent,
requestId: data.requestId, requestId: data.requestId,
withReplies: data.withReplies,
}, },
opts: { opts: {
removeOnComplete: true, removeOnComplete: true,

View File

@@ -30,6 +30,7 @@ import { RoleService } from '@/core/RoleService.js';
import { FeaturedService } from '@/core/FeaturedService.js'; import { FeaturedService } from '@/core/FeaturedService.js';
const FALLBACK = '❤'; const FALLBACK = '❤';
const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16;
const legacies: Record<string, string> = { const legacies: Record<string, string> = {
'like': '👍', 'like': '👍',
@@ -148,13 +149,12 @@ export class ReactionService {
reaction = FALLBACK; reaction = FALLBACK;
} }
} else { } else {
reaction = this.normalize(reaction ?? null); reaction = this.normalize(reaction);
} }
} }
const record: MiNoteReaction = { const record: MiNoteReaction = {
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
noteId: note.id, noteId: note.id,
userId: user.id, userId: user.id,
reaction, reaction,
@@ -188,6 +188,9 @@ export class ReactionService {
await this.notesRepository.createQueryBuilder().update() await this.notesRepository.createQueryBuilder().update()
.set({ .set({
reactions: () => sql, reactions: () => sql,
...(note.reactionAndUserPairCache.length < PER_NOTE_REACTION_USER_PAIR_CACHE_MAX ? {
reactionAndUserPairCache: () => `array_append("reactionAndUserPairCache", '${user.id}/${reaction}')`,
} : {}),
}) })
.where('id = :id', { id: note.id }) .where('id = :id', { id: note.id })
.execute(); .execute();
@@ -294,6 +297,7 @@ export class ReactionService {
await this.notesRepository.createQueryBuilder().update() await this.notesRepository.createQueryBuilder().update()
.set({ .set({
reactions: () => sql, reactions: () => sql,
reactionAndUserPairCache: () => `array_remove("reactionAndUserPairCache", '${user.id}/${exist.reaction}')`,
}) })
.where('id = :id', { id: note.id }) .where('id = :id', { id: note.id })
.execute(); .execute();

View File

@@ -54,7 +54,7 @@ export class RelayService {
@bindThis @bindThis
public async addRelay(inbox: string): Promise<MiRelay> { public async addRelay(inbox: string): Promise<MiRelay> {
const relay = await this.relaysRepository.insert({ const relay = await this.relaysRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
inbox, inbox,
status: 'requesting', status: 'requesting',
}).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0]));

View File

@@ -20,7 +20,7 @@ import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import type { OnApplicationShutdown } from '@nestjs/common'; import type { OnApplicationShutdown } from '@nestjs/common';
export type RolePolicies = { export type RolePolicies = {
@@ -103,7 +103,7 @@ export class RoleService implements OnApplicationShutdown {
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
private idService: IdService, private idService: IdService,
private moderationLogService: ModerationLogService, private moderationLogService: ModerationLogService,
private redisTimelineService: RedisTimelineService, private funoutTimelineService: FunoutTimelineService,
) { ) {
//this.onMessage = this.onMessage.bind(this); //this.onMessage = this.onMessage.bind(this);
@@ -125,7 +125,6 @@ export class RoleService implements OnApplicationShutdown {
if (cached) { if (cached) {
cached.push({ cached.push({
...body, ...body,
createdAt: new Date(body.createdAt),
updatedAt: new Date(body.updatedAt), updatedAt: new Date(body.updatedAt),
lastUsedAt: new Date(body.lastUsedAt), lastUsedAt: new Date(body.lastUsedAt),
}); });
@@ -139,7 +138,6 @@ export class RoleService implements OnApplicationShutdown {
if (i > -1) { if (i > -1) {
cached[i] = { cached[i] = {
...body, ...body,
createdAt: new Date(body.createdAt),
updatedAt: new Date(body.updatedAt), updatedAt: new Date(body.updatedAt),
lastUsedAt: new Date(body.lastUsedAt), lastUsedAt: new Date(body.lastUsedAt),
}; };
@@ -159,7 +157,6 @@ export class RoleService implements OnApplicationShutdown {
if (cached) { if (cached) {
cached.push({ cached.push({
...body, ...body,
createdAt: new Date(body.createdAt),
expiresAt: body.expiresAt ? new Date(body.expiresAt) : null, expiresAt: body.expiresAt ? new Date(body.expiresAt) : null,
}); });
} }
@@ -198,10 +195,10 @@ export class RoleService implements OnApplicationShutdown {
return this.userEntityService.isRemoteUser(user); return this.userEntityService.isRemoteUser(user);
} }
case 'createdLessThan': { case 'createdLessThan': {
return user.createdAt.getTime() > (Date.now() - (value.sec * 1000)); return this.idService.parse(user.id).date.getTime() > (Date.now() - (value.sec * 1000));
} }
case 'createdMoreThan': { case 'createdMoreThan': {
return user.createdAt.getTime() < (Date.now() - (value.sec * 1000)); return this.idService.parse(user.id).date.getTime() < (Date.now() - (value.sec * 1000));
} }
case 'followersLessThanOrEq': { case 'followersLessThanOrEq': {
return user.followersCount <= value.value; return user.followersCount <= value.value;
@@ -382,7 +379,7 @@ export class RoleService implements OnApplicationShutdown {
@bindThis @bindThis
public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise<void> { public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise<void> {
const now = new Date(); const now = Date.now();
const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
@@ -392,7 +389,7 @@ export class RoleService implements OnApplicationShutdown {
}); });
if (existing) { if (existing) {
if (existing.expiresAt && (existing.expiresAt.getTime() < now.getTime())) { if (existing.expiresAt && (existing.expiresAt.getTime() < now)) {
await this.roleAssignmentsRepository.delete({ await this.roleAssignmentsRepository.delete({
roleId: roleId, roleId: roleId,
userId: userId, userId: userId,
@@ -403,8 +400,7 @@ export class RoleService implements OnApplicationShutdown {
} }
const created = await this.roleAssignmentsRepository.insert({ const created = await this.roleAssignmentsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(now),
createdAt: now,
expiresAt: expiresAt, expiresAt: expiresAt,
roleId: roleId, roleId: roleId,
userId: userId, userId: userId,
@@ -474,7 +470,7 @@ export class RoleService implements OnApplicationShutdown {
const redisPipeline = this.redisClient.pipeline(); const redisPipeline = this.redisClient.pipeline();
for (const role of roles) { for (const role of roles) {
this.redisTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline); this.funoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline);
this.globalEventService.publishRoleTimelineStream(role.id, 'note', note); this.globalEventService.publishRoleTimelineStream(role.id, 'note', note);
} }
@@ -485,8 +481,7 @@ export class RoleService implements OnApplicationShutdown {
public async create(values: Partial<MiRole>, moderator?: MiUser): Promise<MiRole> { public async create(values: Partial<MiRole>, moderator?: MiUser): Promise<MiRole> {
const date = new Date(); const date = new Date();
const created = await this.rolesRepository.insert({ const created = await this.rolesRepository.insert({
id: this.idService.genId(), id: this.idService.gen(date.getTime()),
createdAt: date,
updatedAt: date, updatedAt: date,
lastUsedAt: date, lastUsedAt: date,
name: values.name, name: values.name,

View File

@@ -131,7 +131,7 @@ export class SearchService {
await this.meilisearchNoteIndex?.addDocuments([{ await this.meilisearchNoteIndex?.addDocuments([{
id: note.id, id: note.id,
createdAt: note.createdAt.getTime(), createdAt: this.idService.parse(note.id).date.getTime(),
userId: note.userId, userId: note.userId,
userHost: note.userHost, userHost: note.userHost,
channelId: note.channelId, channelId: note.channelId,

View File

@@ -120,8 +120,7 @@ export class SignupService {
if (exist) throw new Error(' the username is already used'); if (exist) throw new Error(' the username is already used');
account = await transactionalEntityManager.save(new MiUser({ account = await transactionalEntityManager.save(new MiUser({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
username: username, username: username,
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: this.utilityService.toPunyNullable(host), host: this.utilityService.toPunyNullable(host),

View File

@@ -68,8 +68,7 @@ export class UserBlockingService implements OnModuleInit {
]); ]);
const blocking = { const blocking = {
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
blocker, blocker,
blockerId: blocker.id, blockerId: blocker.id,
blockee, blockee,

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common'; import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
@@ -28,6 +28,8 @@ import { MetaService } from '@/core/MetaService.js';
import { CacheService } from '@/core/CacheService.js'; import { CacheService } from '@/core/CacheService.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { AccountMoveService } from '@/core/AccountMoveService.js'; import { AccountMoveService } from '@/core/AccountMoveService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import Logger from '../logger.js'; import Logger from '../logger.js';
const logger = new Logger('following/create'); const logger = new Logger('following/create');
@@ -71,6 +73,7 @@ export class UserFollowingService implements OnModuleInit {
private instancesRepository: InstancesRepository, private instancesRepository: InstancesRepository,
private cacheService: CacheService, private cacheService: CacheService,
private utilityService: UtilityService,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService, private idService: IdService,
private queueService: QueueService, private queueService: QueueService,
@@ -81,6 +84,7 @@ export class UserFollowingService implements OnModuleInit {
private webhookService: WebhookService, private webhookService: WebhookService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
private accountMoveService: AccountMoveService, private accountMoveService: AccountMoveService,
private funoutTimelineService: FunoutTimelineService,
private perUserFollowingChart: PerUserFollowingChart, private perUserFollowingChart: PerUserFollowingChart,
private instanceChart: InstanceChart, private instanceChart: InstanceChart,
) { ) {
@@ -91,7 +95,15 @@ export class UserFollowingService implements OnModuleInit {
} }
@bindThis @bindThis
public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> { public async follow(
_follower: { id: MiUser['id'] },
_followee: { id: MiUser['id'] },
{ requestId, silent = false, withReplies }: {
requestId?: string,
silent?: boolean,
withReplies?: boolean,
} = {},
): Promise<void> {
const [follower, followee] = await Promise.all([ const [follower, followee] = await Promise.all([
this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _follower.id }),
this.usersRepository.findOneByOrFail({ id: _followee.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }),
@@ -118,15 +130,16 @@ export class UserFollowingService implements OnModuleInit {
} }
const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id }); const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id });
// フォロー対象が鍵アカウントである or // フォロー対象が鍵アカウントである or
// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである or
// フォロワーがローカルユーザーであり、フォロー対象がサイレンスされているサーバーである
// 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく
if ( if (
followee.isLocked || followee.isLocked ||
(followeeProfile.carefulBot && follower.isBot) || (followeeProfile.carefulBot && follower.isBot) ||
(this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') ||
(this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, follower.host))
) { ) {
let autoAccept = false; let autoAccept = false;
@@ -168,12 +181,12 @@ export class UserFollowingService implements OnModuleInit {
} }
if (!autoAccept) { if (!autoAccept) {
await this.createFollowRequest(follower, followee, requestId); await this.createFollowRequest(follower, followee, requestId, withReplies);
return; return;
} }
} }
await this.insertFollowingDoc(followee, follower, silent); await this.insertFollowingDoc(followee, follower, silent, withReplies);
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
@@ -190,16 +203,17 @@ export class UserFollowingService implements OnModuleInit {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
}, },
silent = false, silent = false,
withReplies?: boolean,
): Promise<void> { ): Promise<void> {
if (follower.id === followee.id) return; if (follower.id === followee.id) return;
let alreadyFollowed = false as boolean; let alreadyFollowed = false as boolean;
await this.followingsRepository.insert({ await this.followingsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
followerId: follower.id, followerId: follower.id,
followeeId: followee.id, followeeId: followee.id,
withReplies: withReplies,
// 非正規化 // 非正規化
followerHost: follower.host, followerHost: follower.host,
@@ -276,8 +290,8 @@ export class UserFollowingService implements OnModuleInit {
this.perUserFollowingChart.update(follower, followee, true); this.perUserFollowingChart.update(follower, followee, true);
} }
// Publish follow event
if (this.userEntityService.isLocalUser(follower) && !silent) { if (this.userEntityService.isLocalUser(follower) && !silent) {
// Publish follow event
this.userEntityService.pack(followee.id, follower, { this.userEntityService.pack(followee.id, follower, {
detail: true, detail: true,
}).then(async packed => { }).then(async packed => {
@@ -290,6 +304,8 @@ export class UserFollowingService implements OnModuleInit {
}); });
} }
}); });
this.funoutTimelineService.purge(`homeTimeline:${follower.id}`);
} }
// Publish followed event // Publish followed event
@@ -343,8 +359,8 @@ export class UserFollowingService implements OnModuleInit {
this.decrementFollowing(following.follower, following.followee); this.decrementFollowing(following.follower, following.followee);
// Publish unfollow event
if (!silent && this.userEntityService.isLocalUser(follower)) { if (!silent && this.userEntityService.isLocalUser(follower)) {
// Publish unfollow event
this.userEntityService.pack(followee.id, follower, { this.userEntityService.pack(followee.id, follower, {
detail: true, detail: true,
}).then(async packed => { }).then(async packed => {
@@ -357,6 +373,8 @@ export class UserFollowingService implements OnModuleInit {
}); });
} }
}); });
this.funoutTimelineService.purge(`homeTimeline:${follower.id}`);
} }
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
@@ -452,6 +470,7 @@ export class UserFollowingService implements OnModuleInit {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
requestId?: string, requestId?: string,
withReplies?: boolean,
): Promise<void> { ): Promise<void> {
if (follower.id === followee.id) return; if (follower.id === followee.id) return;
@@ -465,11 +484,11 @@ export class UserFollowingService implements OnModuleInit {
if (blocked) throw new Error('blocked'); if (blocked) throw new Error('blocked');
const followRequest = await this.followRequestsRepository.insert({ const followRequest = await this.followRequestsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
followerId: follower.id, followerId: follower.id,
followeeId: followee.id, followeeId: followee.id,
requestId, requestId,
withReplies,
// 非正規化 // 非正規化
followerHost: follower.host, followerHost: follower.host,
@@ -554,7 +573,7 @@ export class UserFollowingService implements OnModuleInit {
throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.'); throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.');
} }
await this.insertFollowingDoc(followee, follower); await this.insertFollowingDoc(followee, follower, false, request.withReplies);
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee)); const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee));
@@ -694,4 +713,12 @@ export class UserFollowingService implements OnModuleInit {
}); });
} }
} }
@bindThis
public getFollowees(userId: MiUser['id']) {
return this.followingsRepository.createQueryBuilder('following')
.select('following.followeeId')
.where('following.followerId = :followerId', { followerId: userId })
.getMany();
}
} }

View File

@@ -93,8 +93,7 @@ export class UserListService implements OnApplicationShutdown {
} }
await this.userListMembershipsRepository.insert({ await this.userListMembershipsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
userId: target.id, userId: target.id,
userListId: list.id, userListId: list.id,
userListUserId: list.userId, userListUserId: list.userId,

View File

@@ -26,8 +26,7 @@ export class UserMutingService {
@bindThis @bindThis
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> { public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
await this.mutingsRepository.insert({ await this.mutingsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
expiresAt: expiresAt ?? null, expiresAt: expiresAt ?? null,
muterId: user.id, muterId: user.id,
muteeId: target.id, muteeId: target.id,

View File

@@ -35,6 +35,12 @@ export class UtilityService {
return blockedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); return blockedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
} }
@bindThis
public isSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean {
if (!silencedHosts || host == null) return false;
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
}
@bindThis @bindThis
public extractDbHost(uri: string): string { public extractDbHost(uri: string): string {
const url = new URL(uri); const url = new URL(uri);

View File

@@ -51,7 +51,6 @@ export class WebhookService implements OnApplicationShutdown {
if (body.active) { if (body.active) {
this.webhooks.push({ this.webhooks.push({
...body, ...body,
createdAt: new Date(body.createdAt),
latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
}); });
} }
@@ -62,13 +61,11 @@ export class WebhookService implements OnApplicationShutdown {
if (i > -1) { if (i > -1) {
this.webhooks[i] = { this.webhooks[i] = {
...body, ...body,
createdAt: new Date(body.createdAt),
latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
}; };
} else { } else {
this.webhooks.push({ this.webhooks.push({
...body, ...body,
createdAt: new Date(body.createdAt),
latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
}); });
} }

View File

@@ -164,7 +164,7 @@ export class ApInboxService {
} }
// don't queue because the sender may attempt again when timeout // don't queue because the sender may attempt again when timeout
await this.userFollowingService.follow(actor, followee, activity.id); await this.userFollowingService.follow(actor, followee, { requestId: activity.id });
return 'ok'; return 'ok';
} }
@@ -514,8 +514,7 @@ export class ApInboxService {
if (users.length < 1) return 'skip'; if (users.length < 1) return 'skip';
await this.abuseUserReportsRepository.insert({ await this.abuseUserReportsRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
createdAt: new Date(),
targetUserId: users[0].id, targetUserId: users[0].id,
targetUserHost: users[0].host, targetUserHost: users[0].host,
reporterId: actor.id, reporterId: actor.id,

View File

@@ -27,6 +27,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { isNotNull } from '@/misc/is-not-null.js'; import { isNotNull } from '@/misc/is-not-null.js';
import { IdService } from '@/core/IdService.js';
import { LdSignatureService } from './LdSignatureService.js'; import { LdSignatureService } from './LdSignatureService.js';
import { ApMfmService } from './ApMfmService.js'; import { ApMfmService } from './ApMfmService.js';
import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
@@ -59,6 +60,7 @@ export class ApRendererService {
private userKeypairService: UserKeypairService, private userKeypairService: UserKeypairService,
private apMfmService: ApMfmService, private apMfmService: ApMfmService,
private mfmService: MfmService, private mfmService: MfmService,
private idService: IdService,
) { ) {
} }
@@ -105,7 +107,7 @@ export class ApRendererService {
id: `${this.config.url}/notes/${note.id}/activity`, id: `${this.config.url}/notes/${note.id}/activity`,
actor: this.userEntityService.genLocalUserUri(note.userId), actor: this.userEntityService.genLocalUserUri(note.userId),
type: 'Announce', type: 'Announce',
published: note.createdAt.toISOString(), published: this.idService.parse(note.id).date.toISOString(),
to, to,
cc, cc,
object, object,
@@ -137,7 +139,7 @@ export class ApRendererService {
id: `${this.config.url}/notes/${note.id}/activity`, id: `${this.config.url}/notes/${note.id}/activity`,
actor: this.userEntityService.genLocalUserUri(note.userId), actor: this.userEntityService.genLocalUserUri(note.userId),
type: 'Create', type: 'Create',
published: note.createdAt.toISOString(), published: this.idService.parse(note.id).date.toISOString(),
object, object,
}; };
@@ -437,7 +439,7 @@ export class ApRendererService {
}, },
_misskey_quote: quote, _misskey_quote: quote,
quoteUrl: quote, quoteUrl: quote,
published: note.createdAt.toISOString(), published: this.idService.parse(note.id).date.toISOString(),
to, to,
cc, cc,
inReplyTo, inReplyTo,

View File

@@ -386,7 +386,7 @@ export class ApNoteService {
this.logger.info(`register emoji host=${host}, name=${name}`); this.logger.info(`register emoji host=${host}, name=${name}`);
return await this.emojisRepository.insert({ return await this.emojisRepository.insert({
id: this.idService.genId(), id: this.idService.gen(),
host, host,
name, name,
uri: tag.id, uri: tag.id,

View File

@@ -295,10 +295,9 @@ export class ApPersonService implements OnModuleInit {
// Start transaction // Start transaction
await this.db.transaction(async transactionalEntityManager => { await this.db.transaction(async transactionalEntityManager => {
user = await transactionalEntityManager.save(new MiUser({ user = await transactionalEntityManager.save(new MiUser({
id: this.idService.genId(), id: this.idService.gen(),
avatarId: null, avatarId: null,
bannerId: null, bannerId: null,
createdAt: new Date(),
lastFetchedAt: new Date(), lastFetchedAt: new Date(),
name: truncate(person.name, nameLength), name: truncate(person.name, nameLength),
isLocked: person.manuallyApprovesFollowers, isLocked: person.manuallyApprovesFollowers,
@@ -607,8 +606,7 @@ export class ApPersonService implements OnModuleInit {
for (const note of featuredNotes.filter((note): note is MiNote => note != null)) { for (const note of featuredNotes.filter((note): note is MiNote => note != null)) {
td -= 1000; td -= 1000;
transactionalEntityManager.insert(MiUserNotePining, { transactionalEntityManager.insert(MiUserNotePining, {
id: this.idService.genId(new Date(Date.now() + td)), id: this.idService.gen(Date.now() + td),
createdAt: new Date(),
userId: user.id, userId: user.id,
noteId: note.id, noteId: note.id,
}); });

View File

@@ -9,6 +9,7 @@ import { AppLockService } from '@/core/AppLockService.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import Chart from '../core.js'; import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js'; import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/active-users.js'; import { name, schema } from './entities/active-users.js';
@@ -29,6 +30,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
private appLockService: AppLockService, private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService, private chartLoggerService: ChartLoggerService,
private idService: IdService,
) { ) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
} }
@@ -42,20 +44,21 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
} }
@bindThis @bindThis
public async read(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> { public async read(user: { id: MiUser['id'], host: null }): Promise<void> {
const createdAt = this.idService.parse(user.id).date;
await this.commit({ await this.commit({
'read': [user.id], 'read': [user.id],
'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], 'registeredWithinWeek': (Date.now() - createdAt.getTime() < week) ? [user.id] : [],
'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [], 'registeredWithinMonth': (Date.now() - createdAt.getTime() < month) ? [user.id] : [],
'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [], 'registeredWithinYear': (Date.now() - createdAt.getTime() < year) ? [user.id] : [],
'registeredOutsideWeek': (Date.now() - user.createdAt.getTime() > week) ? [user.id] : [], 'registeredOutsideWeek': (Date.now() - createdAt.getTime() > week) ? [user.id] : [],
'registeredOutsideMonth': (Date.now() - user.createdAt.getTime() > month) ? [user.id] : [], 'registeredOutsideMonth': (Date.now() - createdAt.getTime() > month) ? [user.id] : [],
'registeredOutsideYear': (Date.now() - user.createdAt.getTime() > year) ? [user.id] : [], 'registeredOutsideYear': (Date.now() - createdAt.getTime() > year) ? [user.id] : [],
}); });
} }
@bindThis @bindThis
public async write(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> { public async write(user: { id: MiUser['id'], host: null }): Promise<void> {
await this.commit({ await this.commit({
'write': [user.id], 'write': [user.id],
}); });

View File

@@ -9,6 +9,7 @@ import type { AbuseUserReportsRepository } from '@/models/_.js';
import { awaitAll } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js';
import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -18,6 +19,7 @@ export class AbuseUserReportEntityService {
private abuseUserReportsRepository: AbuseUserReportsRepository, private abuseUserReportsRepository: AbuseUserReportsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -29,7 +31,7 @@ export class AbuseUserReportEntityService {
return await awaitAll({ return await awaitAll({
id: report.id, id: report.id,
createdAt: report.createdAt.toISOString(), createdAt: this.idService.parse(report.id).date.toISOString(),
comment: report.comment, comment: report.comment,
resolved: report.resolved, resolved: report.resolved,
reporterId: report.reporterId, reporterId: report.reporterId,

View File

@@ -9,12 +9,15 @@ import type { AntennasRepository } from '@/models/_.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import type { MiAntenna } from '@/models/Antenna.js'; import type { MiAntenna } from '@/models/Antenna.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
@Injectable() @Injectable()
export class AntennaEntityService { export class AntennaEntityService {
constructor( constructor(
@Inject(DI.antennasRepository) @Inject(DI.antennasRepository)
private antennasRepository: AntennasRepository, private antennasRepository: AntennasRepository,
private idService: IdService,
) { ) {
} }
@@ -26,7 +29,7 @@ export class AntennaEntityService {
return { return {
id: antenna.id, id: antenna.id,
createdAt: antenna.createdAt.toISOString(), createdAt: this.idService.parse(antenna.id).date.toISOString(),
name: antenna.name, name: antenna.name,
keywords: antenna.keywords, keywords: antenna.keywords,
excludeKeywords: antenna.excludeKeywords, excludeKeywords: antenna.excludeKeywords,
@@ -34,6 +37,7 @@ export class AntennaEntityService {
userListId: antenna.userListId, userListId: antenna.userListId,
users: antenna.users, users: antenna.users,
caseSensitive: antenna.caseSensitive, caseSensitive: antenna.caseSensitive,
localOnly: antenna.localOnly,
notify: antenna.notify, notify: antenna.notify,
withReplies: antenna.withReplies, withReplies: antenna.withReplies,
withFile: antenna.withFile, withFile: antenna.withFile,

View File

@@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js';
import type { MiBlocking } from '@/models/Blocking.js'; import type { MiBlocking } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -20,6 +21,7 @@ export class BlockingEntityService {
private blockingsRepository: BlockingsRepository, private blockingsRepository: BlockingsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -32,7 +34,7 @@ export class BlockingEntityService {
return await awaitAll({ return await awaitAll({
id: blocking.id, id: blocking.id,
createdAt: blocking.createdAt.toISOString(), createdAt: this.idService.parse(blocking.id).date.toISOString(),
blockeeId: blocking.blockeeId, blockeeId: blocking.blockeeId,
blockee: this.userEntityService.pack(blocking.blockeeId, me, { blockee: this.userEntityService.pack(blocking.blockeeId, me, {
detail: true, detail: true,

View File

@@ -11,6 +11,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiChannel } from '@/models/Channel.js'; import type { MiChannel } from '@/models/Channel.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { DriveFileEntityService } from './DriveFileEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js';
import { NoteEntityService } from './NoteEntityService.js'; import { NoteEntityService } from './NoteEntityService.js';
import { In } from 'typeorm'; import { In } from 'typeorm';
@@ -38,6 +39,7 @@ export class ChannelEntityService {
private noteEntityService: NoteEntityService, private noteEntityService: NoteEntityService,
private driveFileEntityService: DriveFileEntityService, private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
) { ) {
} }
@@ -81,7 +83,7 @@ export class ChannelEntityService {
return { return {
id: channel.id, id: channel.id,
createdAt: channel.createdAt.toISOString(), createdAt: this.idService.parse(channel.id).date.toISOString(),
lastNotedAt: channel.lastNotedAt ? channel.lastNotedAt.toISOString() : null, lastNotedAt: channel.lastNotedAt ? channel.lastNotedAt.toISOString() : null,
name: channel.name, name: channel.name,
description: channel.description, description: channel.description,

View File

@@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js';
import type { } from '@/models/Blocking.js'; import type { } from '@/models/Blocking.js';
import type { MiClip } from '@/models/Clip.js'; import type { MiClip } from '@/models/Clip.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -23,6 +24,7 @@ export class ClipEntityService {
private clipFavoritesRepository: ClipFavoritesRepository, private clipFavoritesRepository: ClipFavoritesRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -36,7 +38,7 @@ export class ClipEntityService {
return await awaitAll({ return await awaitAll({
id: clip.id, id: clip.id,
createdAt: clip.createdAt.toISOString(), createdAt: this.idService.parse(clip.id).date.toISOString(),
lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null, lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null,
userId: clip.userId, userId: clip.userId,
user: this.userEntityService.pack(clip.user ?? clip.userId), user: this.userEntityService.pack(clip.user ?? clip.userId),

View File

@@ -17,6 +17,7 @@ import { deepClone } from '@/misc/clone.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { isMimeImage } from '@/misc/is-mime-image.js'; import { isMimeImage } from '@/misc/is-mime-image.js';
import { isNotNull } from '@/misc/is-not-null.js'; import { isNotNull } from '@/misc/is-not-null.js';
import { IdService } from '@/core/IdService.js';
import { UtilityService } from '../UtilityService.js'; import { UtilityService } from '../UtilityService.js';
import { VideoProcessingService } from '../VideoProcessingService.js'; import { VideoProcessingService } from '../VideoProcessingService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@@ -44,6 +45,7 @@ export class DriveFileEntityService {
private utilityService: UtilityService, private utilityService: UtilityService,
private driveFolderEntityService: DriveFolderEntityService, private driveFolderEntityService: DriveFolderEntityService,
private videoProcessingService: VideoProcessingService, private videoProcessingService: VideoProcessingService,
private idService: IdService,
) { ) {
} }
@@ -88,7 +90,7 @@ export class DriveFileEntityService {
if (file.type.startsWith('video')) { if (file.type.startsWith('video')) {
if (file.thumbnailUrl) return file.thumbnailUrl; if (file.thumbnailUrl) return file.thumbnailUrl;
return this.videoProcessingService.getExternalVideoThumbnailUrl(file.webpublicUrl ?? file.url ?? file.uri); return this.videoProcessingService.getExternalVideoThumbnailUrl(file.webpublicUrl ?? file.url);
} else if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) { } else if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) {
// 動画ではなくリモートかつメディアプロキシ // 動画ではなくリモートかつメディアプロキシ
return this.getProxiedUrl(file.uri, 'static'); return this.getProxiedUrl(file.uri, 'static');
@@ -143,7 +145,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum') .select('SUM(file.size)', 'sum')
.getRawOne(); .getRawOne();
return parseInt(sum, 10) ?? 0; return parseInt(sum, 10) || 0;
} }
@bindThis @bindThis
@@ -155,7 +157,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum') .select('SUM(file.size)', 'sum')
.getRawOne(); .getRawOne();
return parseInt(sum, 10) ?? 0; return parseInt(sum, 10) || 0;
} }
@bindThis @bindThis
@@ -167,7 +169,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum') .select('SUM(file.size)', 'sum')
.getRawOne(); .getRawOne();
return parseInt(sum, 10) ?? 0; return parseInt(sum, 10) || 0;
} }
@bindThis @bindThis
@@ -179,7 +181,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum') .select('SUM(file.size)', 'sum')
.getRawOne(); .getRawOne();
return parseInt(sum, 10) ?? 0; return parseInt(sum, 10) || 0;
} }
@bindThis @bindThis
@@ -196,7 +198,7 @@ export class DriveFileEntityService {
return await awaitAll<Packed<'DriveFile'>>({ return await awaitAll<Packed<'DriveFile'>>({
id: file.id, id: file.id,
createdAt: file.createdAt.toISOString(), createdAt: this.idService.parse(file.id).date.toISOString(),
name: file.name, name: file.name,
type: file.type, type: file.type,
md5: file.md5, md5: file.md5,
@@ -231,7 +233,7 @@ export class DriveFileEntityService {
return await awaitAll<Packed<'DriveFile'>>({ return await awaitAll<Packed<'DriveFile'>>({
id: file.id, id: file.id,
createdAt: file.createdAt.toISOString(), createdAt: this.idService.parse(file.id).date.toISOString(),
name: file.name, name: file.name,
type: file.type, type: file.type,
md5: file.md5, md5: file.md5,

View File

@@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js';
import type { } from '@/models/Blocking.js'; import type { } from '@/models/Blocking.js';
import type { MiDriveFolder } from '@/models/DriveFolder.js'; import type { MiDriveFolder } from '@/models/DriveFolder.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
@Injectable() @Injectable()
export class DriveFolderEntityService { export class DriveFolderEntityService {
@@ -20,6 +21,8 @@ export class DriveFolderEntityService {
@Inject(DI.driveFilesRepository) @Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository, private driveFilesRepository: DriveFilesRepository,
private idService: IdService,
) { ) {
} }
@@ -38,7 +41,7 @@ export class DriveFolderEntityService {
return await awaitAll({ return await awaitAll({
id: folder.id, id: folder.id,
createdAt: folder.createdAt.toISOString(), createdAt: this.idService.parse(folder.id).date.toISOString(),
name: folder.name, name: folder.name,
parentId: folder.parentId, parentId: folder.parentId,

View File

@@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiFlash } from '@/models/Flash.js'; import type { MiFlash } from '@/models/Flash.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -24,6 +25,7 @@ export class FlashEntityService {
private flashLikesRepository: FlashLikesRepository, private flashLikesRepository: FlashLikesRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -37,7 +39,7 @@ export class FlashEntityService {
return await awaitAll({ return await awaitAll({
id: flash.id, id: flash.id,
createdAt: flash.createdAt.toISOString(), createdAt: this.idService.parse(flash.id).date.toISOString(),
updatedAt: flash.updatedAt.toISOString(), updatedAt: flash.updatedAt.toISOString(),
userId: flash.userId, userId: flash.userId,
user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意 user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意

View File

@@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiFollowing } from '@/models/Following.js'; import type { MiFollowing } from '@/models/Following.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
type LocalFollowerFollowing = MiFollowing & { type LocalFollowerFollowing = MiFollowing & {
@@ -45,6 +46,7 @@ export class FollowingEntityService {
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -83,7 +85,7 @@ export class FollowingEntityService {
return await awaitAll({ return await awaitAll({
id: following.id, id: following.id,
createdAt: following.createdAt.toISOString(), createdAt: this.idService.parse(following.id).date.toISOString(),
followeeId: following.followeeId, followeeId: following.followeeId,
followerId: following.followerId, followerId: following.followerId,
followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, { followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {

View File

@@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiGalleryPost } from '@/models/GalleryPost.js'; import type { MiGalleryPost } from '@/models/GalleryPost.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
import { DriveFileEntityService } from './DriveFileEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js';
@@ -26,6 +27,7 @@ export class GalleryPostEntityService {
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private driveFileEntityService: DriveFileEntityService, private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
) { ) {
} }
@@ -39,7 +41,7 @@ export class GalleryPostEntityService {
return await awaitAll({ return await awaitAll({
id: post.id, id: post.id,
createdAt: post.createdAt.toISOString(), createdAt: this.idService.parse(post.id).date.toISOString(),
updatedAt: post.updatedAt.toISOString(), updatedAt: post.updatedAt.toISOString(),
userId: post.userId, userId: post.userId,
user: this.userEntityService.pack(post.user ?? post.userId, me), user: this.userEntityService.pack(post.user ?? post.userId, me),

View File

@@ -5,7 +5,6 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import type { } from '@/models/Blocking.js';
import type { MiInstance } from '@/models/Instance.js'; import type { MiInstance } from '@/models/Instance.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@@ -43,6 +42,7 @@ export class InstanceEntityService {
description: instance.description, description: instance.description,
maintainerName: instance.maintainerName, maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instance.maintainerEmail,
isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host),
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
faviconUrl: instance.faviconUrl, faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor, themeColor: instance.themeColor,

View File

@@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js'; import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -20,6 +21,7 @@ export class InviteCodeEntityService {
private registrationTicketsRepository: RegistrationTicketsRepository, private registrationTicketsRepository: RegistrationTicketsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -39,7 +41,7 @@ export class InviteCodeEntityService {
id: target.id, id: target.id,
code: target.code, code: target.code,
expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null, expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null,
createdAt: target.createdAt.toISOString(), createdAt: this.idService.parse(target.id).date.toISOString(),
createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null, createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null,
usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null, usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null,
usedAt: target.usedAt ? target.usedAt.toISOString() : null, usedAt: target.usedAt ? target.usedAt.toISOString() : null,

View File

@@ -10,6 +10,7 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
import type { } from '@/models/Blocking.js'; import type { } from '@/models/Blocking.js';
import type { MiModerationLog } from '@/models/ModerationLog.js'; import type { MiModerationLog } from '@/models/ModerationLog.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -19,6 +20,7 @@ export class ModerationLogEntityService {
private moderationLogsRepository: ModerationLogsRepository, private moderationLogsRepository: ModerationLogsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -30,7 +32,7 @@ export class ModerationLogEntityService {
return await awaitAll({ return await awaitAll({
id: log.id, id: log.id,
createdAt: log.createdAt.toISOString(), createdAt: this.idService.parse(log.id).date.toISOString(),
type: log.type, type: log.type,
info: log.info, info: log.info,
userId: log.userId, userId: log.userId,

View File

@@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiMuting } from '@/models/Muting.js'; import type { MiMuting } from '@/models/Muting.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -21,6 +22,7 @@ export class MutingEntityService {
private mutingsRepository: MutingsRepository, private mutingsRepository: MutingsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -33,7 +35,7 @@ export class MutingEntityService {
return await awaitAll({ return await awaitAll({
id: muting.id, id: muting.id,
createdAt: muting.createdAt.toISOString(), createdAt: this.idService.parse(muting.id).date.toISOString(),
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null, expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
muteeId: muting.muteeId, muteeId: muting.muteeId,
mutee: this.userEntityService.pack(muting.muteeId, me, { mutee: this.userEntityService.pack(muting.muteeId, me, {

View File

@@ -5,11 +5,9 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm'; import { In } from 'typeorm';
import * as mfm from 'mfm-js';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import { nyaize } from '@/misc/nyaize.js';
import { awaitAll } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js'; import type { MiNote } from '@/models/Note.js';
@@ -18,6 +16,7 @@ import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepos
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { isNotNull } from '@/misc/is-not-null.js'; import { isNotNull } from '@/misc/is-not-null.js';
import { DebounceLoader } from '@/misc/loader.js'; import { DebounceLoader } from '@/misc/loader.js';
import { IdService } from '@/core/IdService.js';
import type { OnModuleInit } from '@nestjs/common'; import type { OnModuleInit } from '@nestjs/common';
import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { ReactionService } from '../ReactionService.js'; import type { ReactionService } from '../ReactionService.js';
@@ -30,6 +29,7 @@ export class NoteEntityService implements OnModuleInit {
private driveFileEntityService: DriveFileEntityService; private driveFileEntityService: DriveFileEntityService;
private customEmojiService: CustomEmojiService; private customEmojiService: CustomEmojiService;
private reactionService: ReactionService; private reactionService: ReactionService;
private idService: IdService;
private noteLoader = new DebounceLoader(this.findNoteOrFail); private noteLoader = new DebounceLoader(this.findNoteOrFail);
constructor( constructor(
@@ -68,11 +68,12 @@ export class NoteEntityService implements OnModuleInit {
this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService');
this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService');
this.reactionService = this.moduleRef.get('ReactionService'); this.reactionService = this.moduleRef.get('ReactionService');
this.idService = this.moduleRef.get('IdService');
} }
@bindThis @bindThis
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null) { private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null) {
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
let hide = false; let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示 // visibility が specified かつ自分が指定されていなかったら非表示
@@ -82,7 +83,7 @@ export class NoteEntityService implements OnModuleInit {
} else if (meId === packedNote.userId) { } else if (meId === packedNote.userId) {
hide = false; hide = false;
} else { } else {
// 指定されているかどうか // 指定されているかどうか
const specified = packedNote.visibleUserIds!.some((id: any) => meId === id); const specified = packedNote.visibleUserIds!.some((id: any) => meId === id);
if (specified) { if (specified) {
@@ -169,21 +170,31 @@ export class NoteEntityService implements OnModuleInit {
} }
@bindThis @bindThis
private async populateMyReaction(note: MiNote, meId: MiUser['id'], _hint_?: { public async populateMyReaction(note: { id: MiNote['id']; reactions: MiNote['reactions']; reactionAndUserPairCache?: MiNote['reactionAndUserPairCache']; }, meId: MiUser['id'], _hint_?: {
myReactions: Map<MiNote['id'], MiNoteReaction | null>; myReactions: Map<MiNote['id'], string | null>;
}) { }) {
if (_hint_?.myReactions) { if (_hint_?.myReactions) {
const reaction = _hint_.myReactions.get(note.id); const reaction = _hint_.myReactions.get(note.id);
if (reaction) { if (reaction) {
return this.reactionService.convertLegacyReaction(reaction.reaction); return this.reactionService.convertLegacyReaction(reaction);
} else if (reaction === null) { } else {
return undefined; return undefined;
} }
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
} }
// パフォーマンスのためートが作成されてから1秒以上経っていない場合はリアクションを取得しない const reactionsCount = Object.values(note.reactions).reduce((a, b) => a + b, 0);
if (note.createdAt.getTime() + 1000 > Date.now()) { if (reactionsCount === 0) return undefined;
if (note.reactionAndUserPairCache && reactionsCount <= note.reactionAndUserPairCache.length) {
const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId));
if (pair) {
return this.reactionService.convertLegacyReaction(pair.split('/')[1]);
} else {
return undefined;
}
}
// パフォーマンスのためートが作成されてから2秒以上経っていない場合はリアクションを取得しない
if (this.idService.parse(note.id).date.getTime() + 2000 > Date.now()) {
return undefined; return undefined;
} }
@@ -275,8 +286,9 @@ export class NoteEntityService implements OnModuleInit {
options?: { options?: {
detail?: boolean; detail?: boolean;
skipHide?: boolean; skipHide?: boolean;
withReactionAndUserPairCache?: boolean;
_hint_?: { _hint_?: {
myReactions: Map<MiNote['id'], MiNoteReaction | null>; myReactions: Map<MiNote['id'], string | null>;
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>; packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
}; };
}, },
@@ -284,6 +296,7 @@ export class NoteEntityService implements OnModuleInit {
const opts = Object.assign({ const opts = Object.assign({
detail: true, detail: true,
skipHide: false, skipHide: false,
withReactionAndUserPairCache: false,
}, options); }, options);
const meId = me ? me.id : null; const meId = me ? me.id : null;
@@ -309,7 +322,7 @@ export class NoteEntityService implements OnModuleInit {
const packed: Packed<'Note'> = await awaitAll({ const packed: Packed<'Note'> = await awaitAll({
id: note.id, id: note.id,
createdAt: note.createdAt.toISOString(), createdAt: this.idService.parse(note.id).date.toISOString(),
userId: note.userId, userId: note.userId,
user: this.userEntityService.pack(note.user ?? note.userId, me, { user: this.userEntityService.pack(note.user ?? note.userId, me, {
detail: false, detail: false,
@@ -317,13 +330,14 @@ export class NoteEntityService implements OnModuleInit {
text: text, text: text,
cw: note.cw, cw: note.cw,
visibility: note.visibility, visibility: note.visibility,
localOnly: note.localOnly ?? undefined, localOnly: note.localOnly,
reactionAcceptance: note.reactionAcceptance, reactionAcceptance: note.reactionAcceptance,
visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined,
renoteCount: note.renoteCount, renoteCount: note.renoteCount,
repliesCount: note.repliesCount, repliesCount: note.repliesCount,
reactions: this.reactionService.convertLegacyReactions(note.reactions), reactions: this.reactionService.convertLegacyReactions(note.reactions),
reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host), reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host),
reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined,
emojis: host != null ? this.customEmojiService.populateEmojis(note.emojis, host) : undefined, emojis: host != null ? this.customEmojiService.populateEmojis(note.emojis, host) : undefined,
tags: note.tags.length > 0 ? note.tags : undefined, tags: note.tags.length > 0 ? note.tags : undefined,
fileIds: note.fileIds, fileIds: note.fileIds,
@@ -346,41 +360,26 @@ export class NoteEntityService implements OnModuleInit {
reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, { reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, {
detail: false, detail: false,
skipHide: opts.skipHide,
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
_hint_: options?._hint_, _hint_: options?._hint_,
}) : undefined, }) : undefined,
renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, { renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, {
detail: true, detail: true,
skipHide: opts.skipHide,
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
_hint_: options?._hint_, _hint_: options?._hint_,
}) : undefined, }) : undefined,
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
...(meId ? { ...(meId && Object.keys(note.reactions).length > 0 ? {
myReaction: this.populateMyReaction(note, meId, options?._hint_), myReaction: this.populateMyReaction(note, meId, options?._hint_),
} : {}), } : {}),
} : {}), } : {}),
}); });
if (packed.user.isCat && packed.text) {
const tokens = packed.text ? mfm.parse(packed.text) : [];
function nyaizeNode(node: mfm.MfmNode) {
if (node.type === 'quote') return;
if (node.type === 'text') {
node.props.text = nyaize(node.props.text);
}
if (node.children) {
for (const child of node.children) {
nyaizeNode(child);
}
}
}
for (const node of tokens) {
nyaizeNode(node);
}
packed.text = mfm.toString(tokens);
}
if (!opts.skipHide) { if (!opts.skipHide) {
await this.hideNote(packed, meId); await this.hideNote(packed, meId);
} }
@@ -400,18 +399,48 @@ export class NoteEntityService implements OnModuleInit {
if (notes.length === 0) return []; if (notes.length === 0) return [];
const meId = me ? me.id : null; const meId = me ? me.id : null;
const myReactionsMap = new Map<MiNote['id'], MiNoteReaction | null>(); const myReactionsMap = new Map<MiNote['id'], string | null>();
if (meId) { if (meId) {
const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); const idsNeedFetchMyReaction = new Set<MiNote['id']>();
// パフォーマンスのためートが作成されてから1秒以上経っていない場合はリアクションを取得しない
const targets = [...notes.filter(n => n.createdAt.getTime() + 1000 < Date.now()).map(n => n.id), ...renoteIds];
const myReactions = await this.noteReactionsRepository.findBy({
userId: meId,
noteId: In(targets),
});
for (const target of targets) { // パフォーマンスのためートが作成されてから2秒以上経っていない場合はリアクションを取得しない
myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); const oldId = this.idService.gen(Date.now() - 2000);
for (const note of notes) {
if (note.renote && (note.text == null && note.fileIds.length === 0)) { // pure renote
const reactionsCount = Object.values(note.renote.reactions).reduce((a, b) => a + b, 0);
if (reactionsCount === 0) {
myReactionsMap.set(note.renote.id, null);
} else if (reactionsCount <= note.renote.reactionAndUserPairCache.length) {
const pair = note.renote.reactionAndUserPairCache.find(p => p.startsWith(meId));
myReactionsMap.set(note.renote.id, pair ? pair.split('/')[1] : null);
} else {
idsNeedFetchMyReaction.add(note.renote.id);
}
} else {
if (note.id < oldId) {
const reactionsCount = Object.values(note.reactions).reduce((a, b) => a + b, 0);
if (reactionsCount === 0) {
myReactionsMap.set(note.id, null);
} else if (reactionsCount <= note.reactionAndUserPairCache.length) {
const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId));
myReactionsMap.set(note.id, pair ? pair.split('/')[1] : null);
} else {
idsNeedFetchMyReaction.add(note.id);
}
} else {
myReactionsMap.set(note.id, null);
}
}
}
const myReactions = idsNeedFetchMyReaction.size > 0 ? await this.noteReactionsRepository.findBy({
userId: meId,
noteId: In(Array.from(idsNeedFetchMyReaction)),
}) : [];
for (const id of idsNeedFetchMyReaction) {
myReactionsMap.set(id, myReactions.find(reaction => reaction.noteId === id)?.reaction ?? null);
} }
} }

View File

@@ -10,6 +10,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiNoteFavorite } from '@/models/NoteFavorite.js'; import type { MiNoteFavorite } from '@/models/NoteFavorite.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { NoteEntityService } from './NoteEntityService.js'; import { NoteEntityService } from './NoteEntityService.js';
@Injectable() @Injectable()
@@ -19,6 +20,7 @@ export class NoteFavoriteEntityService {
private noteFavoritesRepository: NoteFavoritesRepository, private noteFavoritesRepository: NoteFavoritesRepository,
private noteEntityService: NoteEntityService, private noteEntityService: NoteEntityService,
private idService: IdService,
) { ) {
} }
@@ -31,7 +33,7 @@ export class NoteFavoriteEntityService {
return { return {
id: favorite.id, id: favorite.id,
createdAt: favorite.createdAt.toISOString(), createdAt: this.idService.parse(favorite.id).date.toISOString(),
noteId: favorite.noteId, noteId: favorite.noteId,
note: await this.noteEntityService.pack(favorite.note ?? favorite.noteId, me), note: await this.noteEntityService.pack(favorite.note ?? favorite.noteId, me),
}; };

View File

@@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js';
import type { NoteReactionsRepository } from '@/models/_.js'; import type { NoteReactionsRepository } from '@/models/_.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import type { OnModuleInit } from '@nestjs/common'; import type { OnModuleInit } from '@nestjs/common';
import type { } from '@/models/Blocking.js'; import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
@@ -22,6 +23,7 @@ export class NoteReactionEntityService implements OnModuleInit {
private userEntityService: UserEntityService; private userEntityService: UserEntityService;
private noteEntityService: NoteEntityService; private noteEntityService: NoteEntityService;
private reactionService: ReactionService; private reactionService: ReactionService;
private idService: IdService;
constructor( constructor(
private moduleRef: ModuleRef, private moduleRef: ModuleRef,
@@ -32,6 +34,7 @@ export class NoteReactionEntityService implements OnModuleInit {
//private userEntityService: UserEntityService, //private userEntityService: UserEntityService,
//private noteEntityService: NoteEntityService, //private noteEntityService: NoteEntityService,
//private reactionService: ReactionService, //private reactionService: ReactionService,
//private idService: IdService,
) { ) {
} }
@@ -39,6 +42,7 @@ export class NoteReactionEntityService implements OnModuleInit {
this.userEntityService = this.moduleRef.get('UserEntityService'); this.userEntityService = this.moduleRef.get('UserEntityService');
this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService');
this.reactionService = this.moduleRef.get('ReactionService'); this.reactionService = this.moduleRef.get('ReactionService');
this.idService = this.moduleRef.get('IdService');
} }
@bindThis @bindThis
@@ -57,7 +61,7 @@ export class NoteReactionEntityService implements OnModuleInit {
return { return {
id: reaction.id, id: reaction.id,
createdAt: reaction.createdAt.toISOString(), createdAt: this.idService.parse(reaction.id).date.toISOString(),
user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me), user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
type: this.reactionService.convertLegacyReaction(reaction.reaction), type: this.reactionService.convertLegacyReaction(reaction.reaction),
...(opts.withNote ? { ...(opts.withNote ? {

View File

@@ -13,6 +13,7 @@ import type { MiUser } from '@/models/User.js';
import type { MiPage } from '@/models/Page.js'; import type { MiPage } from '@/models/Page.js';
import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFile } from '@/models/DriveFile.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
import { DriveFileEntityService } from './DriveFileEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js';
@@ -30,6 +31,7 @@ export class PageEntityService {
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private driveFileEntityService: DriveFileEntityService, private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
) { ) {
} }
@@ -85,7 +87,7 @@ export class PageEntityService {
return await awaitAll({ return await awaitAll({
id: page.id, id: page.id,
createdAt: page.createdAt.toISOString(), createdAt: this.idService.parse(page.id).date.toISOString(),
updatedAt: page.updatedAt.toISOString(), updatedAt: page.updatedAt.toISOString(),
userId: page.userId, userId: page.userId,
user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } すると無限ループするので注意 user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } すると無限ループするので注意

View File

@@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js'; import type { MiUser } from '@/models/User.js';
import type { MiRenoteMuting } from '@/models/RenoteMuting.js'; import type { MiRenoteMuting } from '@/models/RenoteMuting.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@@ -21,6 +22,7 @@ export class RenoteMutingEntityService {
private renoteMutingsRepository: RenoteMutingsRepository, private renoteMutingsRepository: RenoteMutingsRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private idService: IdService,
) { ) {
} }
@@ -33,7 +35,7 @@ export class RenoteMutingEntityService {
return await awaitAll({ return await awaitAll({
id: muting.id, id: muting.id,
createdAt: muting.createdAt.toISOString(), createdAt: this.idService.parse(muting.id).date.toISOString(),
muteeId: muting.muteeId, muteeId: muting.muteeId,
mutee: this.userEntityService.pack(muting.muteeId, me, { mutee: this.userEntityService.pack(muting.muteeId, me, {
detail: true, detail: true,

Some files were not shown because too many files have changed in this diff Show More