From 594193ba8aa6e97caff449fc13f5a604696b8974 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sat, 27 Jan 2024 12:26:30 +0900 Subject: [PATCH 01/14] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .config/docker_example.yml | 7 +++++-- .config/example.yml | 7 +++++-- packages/backend/src/config.ts | 7 +++++++ packages/backend/src/core/NoteCreateService.ts | 11 +++++++++++ .../server/api/endpoints/notes/hybrid-timeline.ts | 10 +++++++++- .../src/server/api/endpoints/notes/local-timeline.ts | 10 +++++++++- .../server/api/stream/channels/hybrid-timeline.ts | 12 +++++++++++- .../src/server/api/stream/channels/local-timeline.ts | 12 +++++++++++- 8 files changed, 68 insertions(+), 8 deletions(-) diff --git a/.config/docker_example.yml b/.config/docker_example.yml index acd169bf43..afef8f650b 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -106,7 +106,7 @@ redis: # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── -# You can set scope to local (default value) or global +# You can set scope to local (default value) or global # (include notes from remote). #meilisearch: @@ -185,7 +185,7 @@ proxyRemoteFiles: true signToActivityPubGet: true # For security reasons, uploading attachments from the intranet is prohibited, -# but exceptions can be made from the following settings. Default value is "undefined". +# but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). #allowedPrivateNetworks: [ # '127.0.0.1/32' @@ -193,3 +193,6 @@ signToActivityPubGet: true # Upload or download file size limits (bytes) #maxFileSize: 262144000 + +tagging: + defaultTag: null diff --git a/.config/example.yml b/.config/example.yml index df423c2c83..d2f1660c77 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -118,7 +118,7 @@ redis: # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── -# You can set scope to local (default value) or global +# You can set scope to local (default value) or global # (include notes from remote). #meilisearch: @@ -214,7 +214,7 @@ proxyRemoteFiles: true signToActivityPubGet: true # For security reasons, uploading attachments from the intranet is prohibited, -# but exceptions can be made from the following settings. Default value is "undefined". +# but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). #allowedPrivateNetworks: [ # '127.0.0.1/32' @@ -225,3 +225,6 @@ signToActivityPubGet: true # PID File of master process #pidFile: /tmp/misskey.pid + +tagging: + defaultTag: null diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index b25554b229..281b0892e4 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -56,6 +56,9 @@ type Source = { index: string; scope?: 'local' | 'global' | string[]; }; + tagging: { + defaultTag: string; + }; proxy?: string; proxySmtp?: string; @@ -124,6 +127,9 @@ export type Config = { index: string; scope?: 'local' | 'global' | string[]; } | undefined; + tagging: { + defaultTag: string; + }; proxy: string | undefined; proxySmtp: string | undefined; proxyBypassHosts: string[] | undefined; @@ -261,6 +267,7 @@ export function loadConfig(): Config { perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 500, deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), pidFile: config.pidFile, + tagging: config.tagging, }; } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 30f6d07118..ee30e28e1d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -59,6 +59,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { isReply } from '@/misc/is-reply.js'; import { trackPromise } from '@/misc/promise-tracker.js'; +import { loadConfig } from '@/config.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -913,6 +914,16 @@ export class NoteCreateService implements OnApplicationShutdown { } } + // デフォルトハッシュタグ + const config = loadConfig(); + if (note.visibility === 'public' && note.tags.includes(String(config.tagging.defaultTag))) { + this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); + this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); + if (note.fileIds.length > 0) { + this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r); + } + } + // 自分自身以外への返信 if (isReply(note)) { this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 01adfec7d3..cc0d759075 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -20,6 +20,8 @@ import { MetaService } from '@/core/MetaService.js'; import { MiLocalUser } from '@/models/User.js'; import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js'; import { ApiError } from '../../error.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { loadConfig } from '@/config.js'; export const meta = { tags: ['notes'], @@ -194,10 +196,16 @@ export default class extends Endpoint { // eslint- if (followees.length > 0) { const meOrFolloweeIds = [me.id, ...followees.map(f => f.followeeId)]; qb.where('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds }); - qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { qb.where('note.userId = :meId', { meId: me.id }); + } + + const config = loadConfig(); + let defaultTag:string | null = config.tagging.defaultTag; + if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); + } else { + qb.orWhere(`(note.visibility = 'public') AND ('${normalizeForSearch(defaultTag)}' = any(note.tags)`); } })) .innerJoinAndSelect('note.user', 'user') diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 3fd4dc83fb..dba9261214 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -18,6 +18,8 @@ import { MetaService } from '@/core/MetaService.js'; import { MiLocalUser } from '@/models/User.js'; import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js'; import { ApiError } from '../../error.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { loadConfig } from '@/config.js'; export const meta = { tags: ['notes'], @@ -149,9 +151,15 @@ export default class extends Endpoint { // eslint- withFiles: boolean, withReplies: boolean, }, me: MiLocalUser | null) { + const config = loadConfig(); + let defaultTag:string | null = config.tagging.defaultTag; const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL) AND (note.channelId IS NULL)') + .andWhere( + (defaultTag == null) + ? '(note.visibility = \'public\') AND (note.userHost IS NULL) AND (note.channelId IS NULL)' + : `(note.visibility = 'public') AND ('${normalizeForSearch(defaultTag)}' = any(note.tags) AND (note.channelId IS NULL)` + ) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 957d8b6d41..29bdb8d327 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -13,6 +13,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import Channel, { type MiChannelService } from '../channel.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { loadConfig } from '@/config.js'; class HybridTimelineChannel extends Channel { public readonly chName = 'hybridTimeline'; @@ -22,6 +24,7 @@ class HybridTimelineChannel extends Channel { private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; + private defaultTag: string; constructor( private metaService: MetaService, @@ -43,6 +46,8 @@ class HybridTimelineChannel extends Channel { this.withRenotes = params.withRenotes ?? true; this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; + const config = loadConfig(); + this.defaultTag = config.tagging.defaultTag; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -50,6 +55,11 @@ class HybridTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + let matched = false; + if (this.defaultTag != null) { + const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; + matched = noteTags.includes(normalizeForSearch(this.defaultTag)); + } const isMe = this.user!.id === note.userId; if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; @@ -61,7 +71,7 @@ class HybridTimelineChannel extends Channel { if (!( (note.channelId == null && isMe) || (note.channelId == null && Object.hasOwn(this.following, note.userId)) || - (note.channelId == null && (note.user.host == null && note.visibility === 'public')) || + (note.channelId == null && ((note.user.host == null || matched) && note.visibility === 'public')) || (note.channelId != null && this.followingChannels.has(note.channelId)) )) return; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 888d268d56..00908b9164 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -12,6 +12,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import Channel, { type MiChannelService } from '../channel.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { loadConfig } from '@/config.js'; class LocalTimelineChannel extends Channel { public readonly chName = 'localTimeline'; @@ -20,6 +22,7 @@ class LocalTimelineChannel extends Channel { private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; + private defaultTag: string; constructor( private metaService: MetaService, @@ -41,6 +44,8 @@ class LocalTimelineChannel extends Channel { this.withRenotes = params.withRenotes ?? true; this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; + const config = loadConfig(); + this.defaultTag = config.tagging.defaultTag; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -50,7 +55,12 @@ class LocalTimelineChannel extends Channel { private async onNote(note: Packed<'Note'>) { if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; - if (note.user.host !== null) return; + if (this.defaultTag == null) { + if (note.user.host !== null) return; + } else { + const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; + if (!noteTags.includes(normalizeForSearch(this.defaultTag))) return; + } if (note.visibility !== 'public') return; if (note.channelId != null) return; From 2d7a2a3d6a2737d4631e6d3b8c7c5592be202485 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sat, 27 Jan 2024 19:16:13 +0900 Subject: [PATCH 02/14] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index ee30e28e1d..3c13ef022d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -59,6 +59,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { isReply } from '@/misc/is-reply.js'; import { trackPromise } from '@/misc/promise-tracker.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { loadConfig } from '@/config.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -916,11 +917,14 @@ export class NoteCreateService implements OnApplicationShutdown { // デフォルトハッシュタグ const config = loadConfig(); - if (note.visibility === 'public' && note.tags.includes(String(config.tagging.defaultTag))) { - this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); - this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); - if (note.fileIds.length > 0) { - this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r); + let defaultTag:string | null = config.tagging.defaultTag; + if (defaultTag != null) { + if (note.visibility === 'public' && note.tags.includes(normalizeForSearch(defaultTag))) { + this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); + this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); + if (note.fileIds.length > 0) { + this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r); + } } } From 5667f5affd71318f13b93bc80ac7822af57189c4 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sat, 27 Jan 2024 19:31:58 +0900 Subject: [PATCH 03/14] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 3 ++- .../server/api/endpoints/notes/local-timeline.ts | 15 ++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 3c13ef022d..7f942dc83b 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -919,7 +919,8 @@ export class NoteCreateService implements OnApplicationShutdown { const config = loadConfig(); let defaultTag:string | null = config.tagging.defaultTag; if (defaultTag != null) { - if (note.visibility === 'public' && note.tags.includes(normalizeForSearch(defaultTag))) { + const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; + if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(defaultTag))) { this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index dba9261214..4ad55e3dce 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -155,11 +155,16 @@ export default class extends Endpoint { // eslint- let defaultTag:string | null = config.tagging.defaultTag; const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere( - (defaultTag == null) - ? '(note.visibility = \'public\') AND (note.userHost IS NULL) AND (note.channelId IS NULL)' - : `(note.visibility = 'public') AND ('${normalizeForSearch(defaultTag)}' = any(note.tags) AND (note.channelId IS NULL)` - ) + .andWhere(new Brackets(qb => { + qb.andWhere('note.visibility = \'public\''); + qb.andWhere('note.channelId IS NULL'); + if (defaultTag == null) { + qb.andWhere('note.userHost IS NULL'); + } else { + qb.andWhere(`':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); + } + } + )) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') From b283122951be45f4ca9c9287a3aaf58cc107eb15 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sat, 27 Jan 2024 19:42:35 +0900 Subject: [PATCH 04/14] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index cc0d759075..c6d6c55e9b 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -205,7 +205,7 @@ export default class extends Endpoint { // eslint- if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { - qb.orWhere(`(note.visibility = 'public') AND ('${normalizeForSearch(defaultTag)}' = any(note.tags)`); + qb.orWhere(`(note.visibility = 'public') AND (':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag)} ); } })) .innerJoinAndSelect('note.user', 'user') diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 4ad55e3dce..f4b962f465 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -163,8 +163,7 @@ export default class extends Endpoint { // eslint- } else { qb.andWhere(`':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); } - } - )) + })) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') From de32e55ee4b84adc84df4d130d6aa94b13a65722 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sat, 27 Jan 2024 21:35:53 +0900 Subject: [PATCH 05/14] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 7f942dc83b..4719735dfd 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -59,7 +59,6 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { isReply } from '@/misc/is-reply.js'; import { trackPromise } from '@/misc/promise-tracker.js'; -import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { loadConfig } from '@/config.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; From bc3899f56f8984b01f1a5e091f78113e8fcc6279 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 00:11:04 +0900 Subject: [PATCH 06/14] =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 17 ++++++++++++----- .../api/endpoints/notes/hybrid-timeline.ts | 2 +- .../api/endpoints/notes/local-timeline.ts | 2 +- .../api/stream/channels/hybrid-timeline.ts | 4 ++-- .../api/stream/channels/local-timeline.ts | 4 ++-- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4719735dfd..42d6aa4683 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -59,7 +59,6 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { isReply } from '@/misc/is-reply.js'; import { trackPromise } from '@/misc/promise-tracker.js'; -import { loadConfig } from '@/config.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -321,6 +320,16 @@ export class NoteCreateService implements OnApplicationShutdown { data.localOnly = true; } + // デフォルトハッシュタグ処理 + if (['public', 'home'].includes(data.visibility)) { + if (this.config.tagging.defaultTag != null) { + const tag = `#${this.config.tagging.defaultTag}`; + if (String(data.text).match(tag)) { + data.text = `${data.text}\n\n${tag}`; + } + } + } + if (data.text) { if (data.text.length > DB_MAX_NOTE_TEXT_LENGTH) { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); @@ -915,11 +924,9 @@ export class NoteCreateService implements OnApplicationShutdown { } // デフォルトハッシュタグ - const config = loadConfig(); - let defaultTag:string | null = config.tagging.defaultTag; - if (defaultTag != null) { + if (this.config.tagging.defaultTag != null) { const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; - if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(defaultTag))) { + if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(this.config.tagging.defaultTag))) { this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index c6d6c55e9b..d328f4d846 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -201,7 +201,7 @@ export default class extends Endpoint { // eslint- } const config = loadConfig(); - let defaultTag:string | null = config.tagging.defaultTag; + const defaultTag: string | null = config.tagging.defaultTag; if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index f4b962f465..24f8a303e8 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -152,7 +152,7 @@ export default class extends Endpoint { // eslint- withReplies: boolean, }, me: MiLocalUser | null) { const config = loadConfig(); - let defaultTag:string | null = config.tagging.defaultTag; + const defaultTag: string | null = config.tagging.defaultTag; const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 29bdb8d327..dab99ba400 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -24,7 +24,7 @@ class HybridTimelineChannel extends Channel { private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; - private defaultTag: string; + private defaultTag: string | null; constructor( private metaService: MetaService, @@ -47,7 +47,7 @@ class HybridTimelineChannel extends Channel { this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; const config = loadConfig(); - this.defaultTag = config.tagging.defaultTag; + this.defaultTag = config.tagging.defaultTag; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 00908b9164..e166af413d 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -22,7 +22,7 @@ class LocalTimelineChannel extends Channel { private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; - private defaultTag: string; + private defaultTag: string | null; constructor( private metaService: MetaService, @@ -45,7 +45,7 @@ class LocalTimelineChannel extends Channel { this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; const config = loadConfig(); - this.defaultTag = config.tagging.defaultTag; + this.defaultTag = config.tagging.defaultTag; // Subscribe events this.subscriber.on('notesStream', this.onNote); From 57d2368ba566b29854cb83fb3d128c23a909a0a0 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 10:35:24 +0900 Subject: [PATCH 07/14] =?UTF-8?q?tagging.defaultTag=E3=81=8Cundefined?= =?UTF-8?q?=E3=81=AE=E3=82=B1=E3=83=BC=E3=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 8 ++++---- .../src/server/api/stream/channels/local-timeline.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 42d6aa4683..5460735799 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -322,8 +322,8 @@ export class NoteCreateService implements OnApplicationShutdown { // デフォルトハッシュタグ処理 if (['public', 'home'].includes(data.visibility)) { - if (this.config.tagging.defaultTag != null) { - const tag = `#${this.config.tagging.defaultTag}`; + if (this.config.tagging?.defaultTag != null) { + const tag = `#${this.config.tagging?.defaultTag}`; if (String(data.text).match(tag)) { data.text = `${data.text}\n\n${tag}`; } @@ -924,9 +924,9 @@ export class NoteCreateService implements OnApplicationShutdown { } // デフォルトハッシュタグ - if (this.config.tagging.defaultTag != null) { + if (this.config.tagging?.defaultTag != null) { const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; - if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(this.config.tagging.defaultTag))) { + if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(this.config.tagging?.defaultTag))) { this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index e166af413d..f8d97a6aab 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -45,7 +45,7 @@ class LocalTimelineChannel extends Channel { this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; const config = loadConfig(); - this.defaultTag = config.tagging.defaultTag; + this.defaultTag = config.tagging?.defaultTag; // Subscribe events this.subscriber.on('notesStream', this.onNote); From 167f90f0b9168f618b5d47d9b2d8431fdb95ec7e Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 10:43:43 +0900 Subject: [PATCH 08/14] A space is required before '}' --- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index d328f4d846..917792a7ff 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -205,7 +205,7 @@ export default class extends Endpoint { // eslint- if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { - qb.orWhere(`(note.visibility = 'public') AND (':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag)} ); + qb.orWhere(`(note.visibility = 'public') AND (':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); } })) .innerJoinAndSelect('note.user', 'user') From 5d853f0e012f34a926b0446e6a8d0a0fbd5f32ba Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 10:52:50 +0900 Subject: [PATCH 09/14] =?UTF-8?q?tagging.defaultTag=E3=81=8Cundefined?= =?UTF-8?q?=E3=81=AE=E3=82=B1=E3=83=BC=E3=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 2 +- .../backend/src/server/api/stream/channels/hybrid-timeline.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 917792a7ff..2b25bec1c5 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -201,7 +201,7 @@ export default class extends Endpoint { // eslint- } const config = loadConfig(); - const defaultTag: string | null = config.tagging.defaultTag; + const defaultTag: string | null = config.tagging?.defaultTag; if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 24f8a303e8..642eb57ed9 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -152,7 +152,7 @@ export default class extends Endpoint { // eslint- withReplies: boolean, }, me: MiLocalUser | null) { const config = loadConfig(); - const defaultTag: string | null = config.tagging.defaultTag; + const defaultTag: string | null = config.tagging?.defaultTag; const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index dab99ba400..38196a9c68 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -47,7 +47,7 @@ class HybridTimelineChannel extends Channel { this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; const config = loadConfig(); - this.defaultTag = config.tagging.defaultTag; + this.defaultTag = config.tagging?.defaultTag; // Subscribe events this.subscriber.on('notesStream', this.onNote); From 36cfe398d207aa1c10c05d9752f7e39e78738417 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 10:59:05 +0900 Subject: [PATCH 10/14] =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=83=87=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=81=AE=E8=AA=A4=E3=82=8A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/endpoints/notes/local-timeline.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 642eb57ed9..1c49023073 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -155,15 +155,15 @@ export default class extends Endpoint { // eslint- const defaultTag: string | null = config.tagging?.defaultTag; const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { - qb.andWhere('note.visibility = \'public\''); - qb.andWhere('note.channelId IS NULL'); - if (defaultTag == null) { - qb.andWhere('note.userHost IS NULL'); - } else { - qb.andWhere(`':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); - } - })) + .andWhere(new Brackets(qb => { + qb.andWhere('note.visibility = \'public\''); + qb.andWhere('note.channelId IS NULL'); + if (defaultTag == null) { + qb.andWhere('note.userHost IS NULL'); + } else { + qb.andWhere(`':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); + } + })) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') From f216585421542aeb6210897ac5311b5795d91a6c Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 11:14:44 +0900 Subject: [PATCH 11/14] =?UTF-8?q?=E3=83=A2=E3=83=AD=E3=83=98=E3=82=A4?= =?UTF-8?q?=E3=83=A4=E4=BD=BF=E7=94=A8=E3=81=AE=E5=A0=B4=E5=90=88=E7=AD=89?= =?UTF-8?q?=E3=80=81=E3=82=BF=E3=82=B0=E3=81=AE=E8=BF=BD=E8=A8=98=E3=81=AF?= =?UTF-8?q?=E3=82=AA=E3=83=95=E3=81=AB=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .config/docker_example.yml | 5 +++-- .config/example.yml | 5 +++-- packages/backend/src/config.ts | 12 +++++++----- packages/backend/src/core/NoteCreateService.ts | 14 +++++++------- .../server/api/endpoints/notes/hybrid-timeline.ts | 2 +- .../server/api/endpoints/notes/local-timeline.ts | 2 +- .../server/api/stream/channels/hybrid-timeline.ts | 2 +- .../server/api/stream/channels/local-timeline.ts | 2 +- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.config/docker_example.yml b/.config/docker_example.yml index afef8f650b..292f79e696 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -194,5 +194,6 @@ signToActivityPubGet: true # Upload or download file size limits (bytes) #maxFileSize: 262144000 -tagging: - defaultTag: null +defaultTag: + tag: null + append: true diff --git a/.config/example.yml b/.config/example.yml index d2f1660c77..42f460ed07 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -226,5 +226,6 @@ signToActivityPubGet: true # PID File of master process #pidFile: /tmp/misskey.pid -tagging: - defaultTag: null +defaultTag: + tag: null + append: true diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 281b0892e4..797fe5ae4d 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -56,8 +56,9 @@ type Source = { index: string; scope?: 'local' | 'global' | string[]; }; - tagging: { - defaultTag: string; + defaultTag: { + tag: string; + append: boolean; }; proxy?: string; @@ -127,8 +128,9 @@ export type Config = { index: string; scope?: 'local' | 'global' | string[]; } | undefined; - tagging: { - defaultTag: string; + defaultTag: { + tag: string; + append: boolean; }; proxy: string | undefined; proxySmtp: string | undefined; @@ -267,7 +269,7 @@ export function loadConfig(): Config { perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 500, deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), pidFile: config.pidFile, - tagging: config.tagging, + defaultTag: config.defaultTag, }; } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 5460735799..35694f727d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -320,10 +320,10 @@ export class NoteCreateService implements OnApplicationShutdown { data.localOnly = true; } - // デフォルトハッシュタグ処理 - if (['public', 'home'].includes(data.visibility)) { - if (this.config.tagging?.defaultTag != null) { - const tag = `#${this.config.tagging?.defaultTag}`; + // デフォルトハッシュタグを本文末尾に書き足す + if (this.config.defaultTag?.append && ['public', 'home'].includes(data.visibility)) { + if (this.config.defaultTag?.tag != null) { + const tag = `#${this.config.defaultTag?.tag}`; if (String(data.text).match(tag)) { data.text = `${data.text}\n\n${tag}`; } @@ -923,10 +923,10 @@ export class NoteCreateService implements OnApplicationShutdown { } } - // デフォルトハッシュタグ - if (this.config.tagging?.defaultTag != null) { + // デフォルトハッシュタグを含む投稿は、リモートであってもローカルタイムラインに含める + if (this.config.defaultTag?.tag != null) { const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; - if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(this.config.tagging?.defaultTag))) { + if (note.visibility === 'public' && noteTags.includes(normalizeForSearch(this.config.defaultTag?.tag))) { this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 2b25bec1c5..855cbd1852 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -201,7 +201,7 @@ export default class extends Endpoint { // eslint- } const config = loadConfig(); - const defaultTag: string | null = config.tagging?.defaultTag; + const defaultTag: string | null = config.defaultTag?.tag; if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 1c49023073..8c6b605e67 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -152,7 +152,7 @@ export default class extends Endpoint { // eslint- withReplies: boolean, }, me: MiLocalUser | null) { const config = loadConfig(); - const defaultTag: string | null = config.tagging?.defaultTag; + const defaultTag: string | null = config.defaultTag?.tag; const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 38196a9c68..d6759845ab 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -47,7 +47,7 @@ class HybridTimelineChannel extends Channel { this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; const config = loadConfig(); - this.defaultTag = config.tagging?.defaultTag; + this.defaultTag = config.defaultTag?.tag; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index f8d97a6aab..15b4d60786 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -45,7 +45,7 @@ class LocalTimelineChannel extends Channel { this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; const config = loadConfig(); - this.defaultTag = config.tagging?.defaultTag; + this.defaultTag = config.defaultTag?.tag; // Subscribe events this.subscriber.on('notesStream', this.onNote); From 02211d9071b8d059204bd902229e776425ec30f8 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 11:16:57 +0900 Subject: [PATCH 12/14] =?UTF-8?q?CHANGELOG.md=20=E3=81=AB=E8=BF=BD?= =?UTF-8?q?=E8=A8=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edc9cf7cb..163b1ff674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - ### Client -- +- ### Server - @@ -58,6 +58,7 @@ - Enhance: クリップをエクスポートできるように - Enhance: `/files`のファイルに対してHTTP Rangeリクエストを行えるように - Enhance: `api.json`のOpenAPI Specificationを3.1.0に更新 +- Enhance: デフォルトハッシュタグに対応。 - Fix: `drive/files/update`でファイル名のバリデーションが機能していない問題を修正 - Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更 - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 @@ -105,10 +106,10 @@ ### Note - 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました - 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします -- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。 +- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。 - **影響:** - それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。 + **影響:** + それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。 投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。 1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。 @@ -155,7 +156,7 @@ - Enhance: Unicode 15.0のサポート - Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように - MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました - - 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります + - 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります (例: ` ```js ` → Javascript, ` ```ais ` → AiScript) - Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように - Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる @@ -562,9 +563,9 @@ ### General - 招待機能を改善しました - * 過去に発行した招待コードを確認できるようになりました - * ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました - * 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました + * 過去に発行した招待コードを確認できるようになりました + * ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました + * 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました - ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるようになりました - identicon生成を無効にしてパフォーマンスを向上させることができるようになりました - サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました @@ -727,9 +728,9 @@ Meilisearchの設定に`index`が必要になりました。値はMisskeyサー * 「フォロワーのみ」の投稿は検索結果に表示されません。 - 新規登録前に簡潔なルールをユーザーに表示できる、サーバールール機能を追加 - ユーザーへの自分用メモ機能 - * ユーザーに対して、自分だけが見られるメモを追加できるようになりました。 + * ユーザーに対して、自分だけが見られるメモを追加できるようになりました。 (自分自身に対してもメモを追加できます。) - * ユーザーメニューから追加できます。 + * ユーザーメニューから追加できます。 (デスクトップ表示ではusernameの右側のボタンからも追加可能) - チャンネルに色を設定できるようになりました。各ノートに設定した色のインジケーターが表示されます。 - チャンネルをアーカイブできるようになりました。 From 5739246600f1507b16b7a86948da188d91fc6a1c Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sun, 28 Jan 2024 11:42:34 +0900 Subject: [PATCH 13/14] =?UTF-8?q?CHANGELOG.md=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 163b1ff674..5d8af61893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - ### Client -- +- ### Server - @@ -58,7 +58,7 @@ - Enhance: クリップをエクスポートできるように - Enhance: `/files`のファイルに対してHTTP Rangeリクエストを行えるように - Enhance: `api.json`のOpenAPI Specificationを3.1.0に更新 -- Enhance: デフォルトハッシュタグに対応。 +- Enhance: デフォルトハッシュタグ実装 (#13097) - Fix: `drive/files/update`でファイル名のバリデーションが機能していない問題を修正 - Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更 - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 @@ -106,10 +106,10 @@ ### Note - 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました - 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします -- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。 +- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。 - **影響:** - それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。 + **影響:** + それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。 投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。 1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。 @@ -156,7 +156,7 @@ - Enhance: Unicode 15.0のサポート - Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように - MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました - - 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります + - 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります (例: ` ```js ` → Javascript, ` ```ais ` → AiScript) - Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように - Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる @@ -563,9 +563,9 @@ ### General - 招待機能を改善しました - * 過去に発行した招待コードを確認できるようになりました - * ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました - * 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました + * 過去に発行した招待コードを確認できるようになりました + * ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました + * 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました - ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるようになりました - identicon生成を無効にしてパフォーマンスを向上させることができるようになりました - サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました @@ -728,9 +728,9 @@ Meilisearchの設定に`index`が必要になりました。値はMisskeyサー * 「フォロワーのみ」の投稿は検索結果に表示されません。 - 新規登録前に簡潔なルールをユーザーに表示できる、サーバールール機能を追加 - ユーザーへの自分用メモ機能 - * ユーザーに対して、自分だけが見られるメモを追加できるようになりました。 + * ユーザーに対して、自分だけが見られるメモを追加できるようになりました。 (自分自身に対してもメモを追加できます。) - * ユーザーメニューから追加できます。 + * ユーザーメニューから追加できます。 (デスクトップ表示ではusernameの右側のボタンからも追加可能) - チャンネルに色を設定できるようになりました。各ノートに設定した色のインジケーターが表示されます。 - チャンネルをアーカイブできるようになりました。 From f4fb82ba231693e96d25dfe97dc834ca719dcba0 Mon Sep 17 00:00:00 2001 From: Tatsuya Koishi Date: Sat, 27 Jul 2024 19:04:53 +0900 Subject: [PATCH 14/14] https://github.com/misskey-dev/misskey/pull/13098#pullrequestreview-1847388785 --- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 3acfe634de..a22f43c79b 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -206,7 +206,7 @@ export default class extends Endpoint { // eslint- if (defaultTag == null) { qb.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); } else { - qb.orWhere(`(note.visibility = 'public') AND (':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); + qb.orWhere(`(note.visibility = 'public') AND (:t <@ note.tags`, { t: normalizeForSearch(defaultTag) }); } })) .innerJoinAndSelect('note.user', 'user') diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 0c812d2775..a2806c1787 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -161,7 +161,7 @@ export default class extends Endpoint { // eslint- if (defaultTag == null) { qb.andWhere('note.userHost IS NULL'); } else { - qb.andWhere(`':t' = any(note.tags)`, { t: normalizeForSearch(defaultTag) }); + qb.andWhere(`:t <@ note.tags`, { t: normalizeForSearch(defaultTag) }); } })) .innerJoinAndSelect('note.user', 'user')