From 140daecf2fac4e182410b894da09fd2074034078 Mon Sep 17 00:00:00 2001 From: Leah Date: Sun, 19 Jan 2025 19:43:39 +0100 Subject: [PATCH 1/3] split speak as cat and is cat. Also add option for other people to disable it so they dont have to see cat speak --- locales/en-US.yml | 3 +++ locales/index.d.ts | 14 +++++++++++++- locales/ja-JP.yml | 3 +++ .../backend/migration/1696386694000-speakAsCat.js | 12 ++++++++++++ .../src/core/activitypub/ApRendererService.ts | 1 + .../backend/src/core/activitypub/misc/contexts.ts | 3 +++ .../src/core/activitypub/models/ApPersonService.ts | 4 +++- .../backend/src/core/entities/UserEntityService.ts | 1 + packages/backend/src/models/User.ts | 6 ++++++ packages/backend/src/models/json-schema/user.ts | 4 ++++ .../backend/src/server/api/endpoints/i/update.ts | 2 ++ packages/backend/test/e2e/users.ts | 4 ++++ packages/frontend/.storybook/fakes.ts | 1 + .../components/global/MkMisskeyFlavoredMarkdown.ts | 4 ++-- packages/frontend/src/pages/settings/general.vue | 2 ++ .../src/pages/settings/preferences-backups.vue | 1 + packages/frontend/src/pages/settings/profile.vue | 3 +++ packages/frontend/src/store.ts | 4 ++++ 18 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 packages/backend/migration/1696386694000-speakAsCat.js diff --git a/locales/en-US.yml b/locales/en-US.yml index 3cf165e3e..830c73f0b 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -175,6 +175,8 @@ flagAsBot: "Mark this account as a bot" flagAsBotDescription: "Enable this option if this account is controlled by a program. If enabled, it will act as a flag for other developers to prevent endless interaction chains with other bots and adjust Misskey's internal systems to treat this account as a bot." flagAsCat: "Mark this account as a cat" flagAsCatDescription: "Enable this option to mark this account as a cat." +flagSpeakAsCat: "Speak as a cat" +flagSpeakAsCatDescription: "Your posts will get nyanified when in cat mode. If this isn't working, then please check that you dont have 'Disable cat speak' on under General/Note Display" flagShowTimelineReplies: "Show replies in timeline" flagShowTimelineRepliesDescription: "Shows replies of users to notes of other users in the timeline if turned on." autoAcceptFollowed: "Automatically approve follow requests from users you're following" @@ -762,6 +764,7 @@ noCrawleDescription: "Ask search engines to not index your profile page, notes, lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes will be visible to anyone, even if you require followers to be manually approved." alwaysMarkSensitive: "Mark as sensitive by default" loadRawImages: "Load original images instead of showing thumbnails" +disableCatSpeak: "Disable cat speak" disableShowingAnimatedImages: "Don't play animated images" highlightSensitiveMedia: "Highlight sensitive media" verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification." diff --git a/locales/index.d.ts b/locales/index.d.ts index da14073e8..870a49a1a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -724,6 +724,14 @@ export interface Locale extends ILocale { * にゃにゃにゃ?? */ "flagAsCatDescription": string; + /** + * 猫語で話す + */ + "flagSpeakAsCat": string; + /** + * 有効にすると、あなたの投稿の 「な」を「にゃ」にします。 + */ + "flagSpeakAsCatDescription": string; /** * タイムラインにノートへの返信を表示する */ @@ -3064,6 +3072,10 @@ export interface Locale extends ILocale { * 添付画像のサムネイルをオリジナル画質にする */ "loadRawImages": string; + /** + * 猫の話し方を無効にする + */ + "disableCatSpeak": string; /** * アニメーション画像を再生しない */ @@ -6843,7 +6855,7 @@ export interface Locale extends ILocale { }; "_tutorialCompleted": { /** - * Misskey初心者講座 修了証 + * Forkey初心者講座 修了証 */ "title": string; /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0e4770453..1433ffa3e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -177,6 +177,8 @@ flagAsBot: "Botとして設定" flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。" flagAsCat: "にゃああああああああああああああ!!!!!!!!!!!!" flagAsCatDescription: "にゃにゃにゃ??" +flagSpeakAsCat: "猫語で話す" +flagSpeakAsCatDescription: "有効にすると、あなたの投稿の 「な」を「にゃ」にします。" flagShowTimelineReplies: "タイムラインにノートへの返信を表示する" flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。" autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認" @@ -762,6 +764,7 @@ noCrawleDescription: "外部の検索エンジンにあなたのユーザーペ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。" alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする" +disableCatSpeak: "猫の話し方を無効にする" disableShowingAnimatedImages: "アニメーション画像を再生しない" highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示" verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。" diff --git a/packages/backend/migration/1696386694000-speakAsCat.js b/packages/backend/migration/1696386694000-speakAsCat.js new file mode 100644 index 000000000..d68b9401b --- /dev/null +++ b/packages/backend/migration/1696386694000-speakAsCat.js @@ -0,0 +1,12 @@ +export class SpeakAsCat1696386694000 { + name = "SpeakAsCat1696386694000"; + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "speakAsCat" boolean NOT NULL DEFAULT true`); + await queryRunner.query(`COMMENT ON COLUMN "user"."speakAsCat" IS 'Whether to speak as a cat if chosen.'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "speakAsCat"`); + } +} diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 40d06b3ae..f91f75090 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -518,6 +518,7 @@ export class ApRendererService { discoverable: user.isExplorable, publicKey: this.renderKey(user, keypair, '#main-key'), isCat: user.isCat, + speakAsCat: user.speakAsCat, attachment: attachment.length ? attachment : undefined, }; diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 653fcdcb8..db1e8535f 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -553,6 +553,9 @@ export const CONTEXTS: (string | Context)[] = [ '_misskey_votes': 'misskey:_misskey_votes', '_misskey_summary': 'misskey:_misskey_summary', 'isCat': 'misskey:isCat', + // Firefish + firefish: "https://joinfirefish.org/ns#", + speakAsCat: "firefish:speakAsCat", // vcard vcard: 'http://www.w3.org/2006/vcard/ns#', } satisfies Context, diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index a4d0ef60d..24b3e0523 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -389,6 +389,7 @@ export class ApPersonService implements OnModuleInit { tags, isBot, isCat: (person as any).isCat === true, + speakAsCat: (person as any).speakAsCat != null ? (person as any).speakAsCat === true : (person as any).isCat === true, emojis, })) as MiRemoteUser; @@ -562,12 +563,13 @@ export class ApPersonService implements OnModuleInit { tags, isBot: getApType(object) === 'Service' || getApType(object) === 'Application', isCat: (person as any).isCat === true, + speakAsCat: (person as any).speakAsCat != null ? (person as any).speakAsCat === true : (person as any).isCat === true, isLocked: person.manuallyApprovesFollowers, movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, ...((policy.canUpdateAvatar || policy.canUpdateBanner) ? await this.resolveAvatarAndBanner(exist, policy.canUpdateAvatar ? person.icon : exist.avatarUrl, policy.canUpdateBanner ? person.image : exist.bannerUrl).catch(() => ({})) : {}), - } as Partial & Pick; + } as Partial & Pick; const moving = ((): boolean => { // 移行先がない→ある diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index e366a86b4..9e7e774a9 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -489,6 +489,7 @@ export class UserEntityService implements OnModuleInit { }))) : [], isBot: user.isBot, isCat: user.isCat, + speakAsCat: user.speakAsCat, instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? { name: instance.name, softwareName: instance.softwareName, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 7db66f1f8..05d10008f 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -186,6 +186,12 @@ export class MiUser { }) public isCat: boolean; + @Column('boolean', { + default: true, + comment: 'Whether the User speaks in nya.', + }) + public speakAsCat: boolean; + @Column('boolean', { default: false, comment: 'Whether the User is the root.', diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 1fc71c2ac..d7e41b13c 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -115,6 +115,10 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: true, }, + speakAsCat: { + type: 'boolean', + nullable: false, optional: true, + }, instance: { type: 'object', nullable: false, optional: true, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 62d64a8ff..eb537a59d 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -187,6 +187,7 @@ export const paramDef = { preventAiLearning: { type: 'boolean' }, isBot: { type: 'boolean' }, isCat: { type: 'boolean' }, + speakAsCat: { type: 'boolean' }, injectFeaturedNote: { type: 'boolean' }, receiveAnnouncementEmail: { type: 'boolean' }, alwaysMarkNsfw: { type: 'boolean' }, @@ -341,6 +342,7 @@ export default class extends Endpoint { // eslint- if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning; if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat; + if (typeof ps.speakAsCat === 'boolean') updates.speakAsCat = ps.speakAsCat; if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.alwaysMarkNsfw === 'boolean') { diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index c7aca5958..65a29033a 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -40,6 +40,7 @@ describe('ユーザー', () => { avatarDecorations: user.avatarDecorations, isBot: user.isBot, isCat: user.isCat, + speakAsCat: user.speakAsCat, instance: user.instance, emojis: user.emojis, onlineStatus: user.onlineStatus, @@ -311,6 +312,7 @@ describe('ユーザー', () => { assert.deepStrictEqual(response.avatarDecorations, []); assert.strictEqual(response.isBot, false); assert.strictEqual(response.isCat, false); + assert.strictEqual(response.speakAsCat, false); assert.strictEqual(response.instance, undefined); assert.deepStrictEqual(response.emojis, {}); assert.strictEqual(response.onlineStatus, 'unknown'); @@ -446,6 +448,8 @@ describe('ユーザー', () => { { parameters: () => ({ isBot: false }) }, { parameters: () => ({ isCat: true }) }, { parameters: () => ({ isCat: false }) }, + { parameters: () => ({ speakAsCat: true }) }, + { parameters: () => ({ speakAsCat: false }) }, { parameters: () => ({ injectFeaturedNote: true }) }, { parameters: () => ({ injectFeaturedNote: false }) }, { parameters: () => ({ receiveAnnouncementEmail: true }) }, diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index 60a8cabe1..454525580 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -100,6 +100,7 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi isBlocking: false, isBot: false, isCat: false, + speakAsCat: false, isFollowed: false, isFollowing: false, isLocked: false, diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 0bcd0d23f..6a4b703b9 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -55,7 +55,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext{{ i18n.ts.showReactionsCount }} {{ i18n.ts.showGapBetweenNotesInTimeline }} {{ i18n.ts.loadRawImages }} + {{ i18n.ts.disableCatSpeak }} @@ -298,6 +299,7 @@ const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); +const disableCatSpeak = computed(defaultStore.makeGetterSetter('disableCatSpeak')); const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const warnMissingAltText = computed(defaultStore.makeGetterSetter('warnMissingAltText')); diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 58380c361..5fa6e9542 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -78,6 +78,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'warnMissingAltText', 'imageNewTab', 'dataSaver', + 'disableCatSpeak', 'disableShowingAnimatedImages', 'emojiStyle', 'disableDrawer', diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index dfeba525a..308736725 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -171,6 +171,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.flagAsCat }} + {{ i18n.ts.flagSpeakAsCat }} {{ i18n.ts.flagAsBot }}
@@ -220,6 +221,7 @@ const profile = reactive({ lang: $i.lang, isBot: $i.isBot ?? false, isCat: $i.isCat ?? false, + speakAsCat: $i.speakAsCat ?? false, }); watch(() => profile, () => { @@ -304,6 +306,7 @@ function save() { lang: profile.lang || null, isBot: !!profile.isBot, isCat: !!profile.isCat, + speakAsCat: !!profile.speakAsCat, }); globalEvents.emit('requestClearPageCache'); claimAchievement('profileFilled'); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index b44e1d9c4..b4dbb1984 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -259,6 +259,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: window.matchMedia('(prefers-reduced-motion)').matches, }, + disableCatSpeak: { + where: 'account', + default: false, + }, emojiStyle: { where: 'device', default: 'twemoji', // twemoji / fluentEmoji / native -- 2.45.2 From f0de9000a093cf25dcbddf6f7ccb5204106da9fc Mon Sep 17 00:00:00 2001 From: Leah Date: Mon, 3 Feb 2025 16:04:12 +0100 Subject: [PATCH 2/3] fix mix of tabs and spaces --- packages/backend/src/models/User.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 05d10008f..41a5842eb 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -188,9 +188,9 @@ export class MiUser { @Column('boolean', { default: true, - comment: 'Whether the User speaks in nya.', - }) - public speakAsCat: boolean; + comment: 'Whether the User speaks in nya.', + }) + public speakAsCat: boolean; @Column('boolean', { default: false, -- 2.45.2 From 12a9dab2754649126d6c3fc037eedf2acdd394c7 Mon Sep 17 00:00:00 2001 From: Leah Date: Tue, 4 Feb 2025 14:39:49 +0100 Subject: [PATCH 3/3] fix tests? --- packages/backend/src/models/User.ts | 2 +- packages/misskey-js/src/autogen/types.ts | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 41a5842eb..ea751be4a 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -187,7 +187,7 @@ export class MiUser { public isCat: boolean; @Column('boolean', { - default: true, + default: false, comment: 'Whether the User speaks in nya.', }) public speakAsCat: boolean; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 41706def4..7b6bcca1a 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3853,6 +3853,7 @@ export type components = { }[]; isBot?: boolean; isCat?: boolean; + speakAsCat?: boolean; instance?: { name: string | null; softwareName: string | null; @@ -4702,7 +4703,7 @@ export type components = { blockee: components['schemas']['UserDetailedNotMe']; }; Hashtag: { - /** @example misskey */ + /** @example forkey */ tag: string; mentionedUsersCount: number; mentionedLocalUsersCount: number; @@ -4885,7 +4886,7 @@ export type components = { isNotResponding: boolean; isSuspended: boolean; isBlocked: boolean; - /** @example misskey */ + /** @example forkey */ softwareName: string | null; softwareVersion: string | null; /** @example true */ @@ -5239,6 +5240,8 @@ export type components = { enableTurnstile: boolean; turnstileSiteKey: string | null; googleAnalyticsId: string | null; + enableFC: boolean; + fcSiteKey: string | null; swPublickey: string | null; /** @default /assets/ai.png */ mascotImageUrl: string; @@ -5388,6 +5391,8 @@ export type operations = { enableTurnstile: boolean; turnstileSiteKey: string | null; googleAnalyticsId: string | null; + enableFC: boolean; + fcSiteKey: string | null; swPublickey: string | null; /** @default /assets/ai.png */ mascotImageUrl: string | null; @@ -5414,6 +5419,7 @@ export type operations = { mcaptchaSecretKey: string | null; recaptchaSecretKey: string | null; turnstileSecretKey: string | null; + fcSecretKey: string | null; sensitiveMediaDetection: string; sensitiveMediaDetectionSensitivity: string; setSensitiveFlagAutomatically: boolean; @@ -10319,6 +10325,9 @@ export type operations = { enableTurnstile?: boolean; turnstileSiteKey?: string | null; turnstileSecretKey?: string | null; + enableFC?: boolean; + fcSiteKey?: string | null; + fcSecretKey?: string | null; googleAnalyticsId?: string | null; /** @enum {string} */ sensitiveMediaDetection?: 'none' | 'all' | 'local' | 'remote'; @@ -21747,6 +21756,7 @@ export type operations = { preventAiLearning?: boolean; isBot?: boolean; isCat?: boolean; + speakAsCat?: boolean; injectFeaturedNote?: boolean; receiveAnnouncementEmail?: boolean; alwaysMarkNsfw?: boolean; @@ -30841,3 +30851,4 @@ export type operations = { }; }; }; + -- 2.45.2