Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

みつける>ハイライトのふぁぼすたー機能 #14977

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/backend/migration/1731722596000-favstar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class Favstar17317225960000 {
constructor() {
this.name = 'Favstar17317225960000';
}
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "highlightRateFactor" integer NOT NULL DEFAULT 30`);
await queryRunner.query(`ALTER TABLE "meta" ADD "highlightMidPopularityThreshold" integer NOT NULL DEFAULT 3`);
await queryRunner.query(`ALTER TABLE "meta" ADD "highlightHighPopularityThreashold" integer NOT NULL DEFAULT 5`)
await queryRunner.query(`ALTER TABLE "meta" ADD "highlightExcludeEmojis" text NOT NULL DEFAULT ''`)
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "highlightRateFactor"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "highlightMidPopularityThreshold"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "highlightHighPopularityThreashold"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "highlightExcludeEmojis"`);
}
}
2 changes: 1 addition & 1 deletion packages/backend/src/core/NoteCreateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ export class NoteCreateService implements OnApplicationShutdown {
.execute();

// 30%の確率、3日以内に投稿されたノートの場合ハイライト用ランキング更新
if (Math.random() < 0.3 && (Date.now() - this.idService.parse(renote.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3) {
if (Math.random() <= (this.meta.highlightRateFactor / 100) && (Date.now() - this.idService.parse(renote.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3) {
if (renote.channelId != null) {
if (renote.replyId == null) {
this.featuredService.updateInChannelNotesRanking(renote.channelId, renote.id, 5);
Expand Down
13 changes: 10 additions & 3 deletions packages/backend/src/core/ReactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Inject, Injectable } from '@nestjs/common';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository, MiMeta } from '@/models/_.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
Expand Down Expand Up @@ -123,7 +123,7 @@ export class ReactionService {
}

let reaction = _reaction ?? FALLBACK;

if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) {
reaction = '\u2764';
} else if (_reaction != null) {
Expand Down Expand Up @@ -171,6 +171,8 @@ export class ReactionService {
reaction,
};

Logger.error('I\'m ' + reaction);

try {
await this.noteReactionsRepository.insert(record);
} catch (e) {
Expand Down Expand Up @@ -209,9 +211,14 @@ export class ReactionService {
.execute();
}

const excludeEmojis = this.meta.highlightExcludeEmojis.split(/:\n/).filter(v => v);
Logger.error('I\'m ' + reaction);

Logger.error(excludeEmojis, !excludeEmojis.includes(reaction));
// 30%の確率、セルフではない、3日以内に投稿されたノートの場合ハイライト用ランキング更新
if (
Math.random() < 0.3 &&
!excludeEmojis.includes(reaction) &&
Math.random() <= (this.meta.highlightRateFactor / 100) &&
note.userId !== user.id &&
(Date.now() - this.idService.parse(note.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3
) {
Expand Down
16 changes: 14 additions & 2 deletions packages/backend/src/core/entities/MetaEntityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';

type HighlightExtention = {
highlightRateFactor: number,
highlightMidPopularityThreshold: number,
highlightHighPopularityThreashold: number,
highlightExcludeEmojis: string
}

@Injectable()
export class MetaEntityService {
constructor(
Expand All @@ -34,7 +41,7 @@ export class MetaEntityService {
) { }

@bindThis
public async pack(meta?: MiMeta): Promise<Packed<'MetaLite'>> {
public async pack(meta?: MiMeta): Promise<Packed<'MetaLite'> & HighlightExtention> {
let instance = meta;

if (!instance) {
Expand Down Expand Up @@ -67,7 +74,7 @@ export class MetaEntityService {
}
}

const packed: Packed<'MetaLite'> = {
const packed: Packed<'MetaLite'> & HighlightExtention = {
maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail,

Expand Down Expand Up @@ -132,6 +139,11 @@ export class MetaEntityService {
enableUrlPreview: instance.urlPreviewEnabled,
noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local',
maxFileSize: this.config.maxFileSize,

highlightRateFactor: instance.highlightRateFactor,
highlightMidPopularityThreshold: instance.highlightMidPopularityThreshold,
highlightHighPopularityThreashold: instance.highlightHighPopularityThreashold,
highlightExcludeEmojis: instance.highlightExcludeEmojis,
};

return packed;
Expand Down
9 changes: 9 additions & 0 deletions packages/backend/src/models/Meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,4 +658,13 @@ export class MiMeta {
default: '{}',
})
public federationHosts: string[];

@Column('integer', { default: 30 })
public highlightRateFactor: number;
@Column('integer', { default: 3 })
public highlightMidPopularityThreshold: number;
@Column('integer', { default: 5 })
public highlightHighPopularityThreashold: number;
@Column('text', { default: '' })
public highlightExcludeEmojis: string;
}
22 changes: 22 additions & 0 deletions packages/backend/src/server/api/endpoints/admin/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,24 @@ export const meta = {
optional: false, nullable: false,
},
},

highlightRateFactor: {
type: 'number',
optinal: false, nullable: false,
},

highlightMidPopularityThreshold: {
type: 'number',
optinal: false, nullable: false,
},
highlightHighPopularityThreashold: {
type: 'number',
optinal: false, nullable: false,
},
highlightExcludeEmojis: {
type: 'string',
optinal: false, nullable: false,
},
},
},
} as const;
Expand Down Expand Up @@ -662,6 +680,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl,
federation: instance.federation,
federationHosts: instance.federationHosts,
highlightRateFactor: instance.highlightRateFactor,
highlightMidPopularityThreshold: instance.highlightMidPopularityThreshold,
highlightHighPopularityThreashold: instance.highlightHighPopularityThreashold,
highlightExcludeEmojis: instance.highlightExcludeEmojis,
};
});
}
Expand Down
20 changes: 20 additions & 0 deletions packages/backend/src/server/api/endpoints/admin/update-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ export const paramDef = {
type: 'string',
},
},
highlightRateFactor: {
type: 'number',
},

highlightMidPopularityThreshold: {
type: 'number',
},
highlightHighPopularityThreashold: {
type: 'number',
},
highlightExcludeEmojis: {
type: 'string',
},
},
required: [],
} as const;
Expand Down Expand Up @@ -673,6 +686,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.federationHosts = ps.federationHosts.filter(Boolean).map(x => x.toLowerCase());
}

if (ps.highlightRateFactor) {
set.highlightRateFactor = ps.highlightRateFactor;
set.highlightMidPopularityThreshold = ps.highlightMidPopularityThreshold;
set.highlightHighPopularityThreashold = ps.highlightHighPopularityThreashold;
set.highlightExcludeEmojis = ps.highlightExcludeEmojis;
}

const before = await this.metaService.fetch(true);

await this.metaService.update(set);
Expand Down
24 changes: 21 additions & 3 deletions packages/frontend/src/components/MkNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
</p>
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
<div :class="$style.text">
<div :class="[$style.text, {[$style.akafav]:(featured && note.reactionCount >= highlightPopularityThreshold.highPopularity ), [$style.aofav]: featured && note.reactionCount >= highlightPopularityThreshold.midPopularity && note.reactionCount < highlightPopularityThreshold.highPopularity }]">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
<MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
<Mfm
Expand Down Expand Up @@ -205,7 +205,7 @@
import { getNoteSummary } from '@/scripts/get-note-summary.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { isEnabledUrlPreview } from '@/instance.js';
import { instance, isEnabledUrlPreview } from '@/instance.js';
import { type Keymap } from '@/scripts/hotkey.js';
import { focusPrev, focusNext } from '@/scripts/focus.js';
import { getAppearNote } from '@/scripts/get-appear-note.js';
Expand All @@ -214,9 +214,11 @@
note: Misskey.entities.Note;
pinned?: boolean;
mock?: boolean;
withHardMute?: boolean;
withHardMute?: boolean;
featured: boolean;
}>(), {
mock: false,
featured: false,
});

provide('mock', props.mock);
Expand All @@ -233,6 +235,13 @@

const note = ref(deepClone(props.note));


Check failure on line 238 in packages/frontend/src/components/MkNote.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

More than 1 blank line not allowed
const highlightPopularityThreshold = computed(() => {
return {
highPopularity: instance.highlightHighPopularityThreashold,
midPopularity: instance.highlightMidPopularityThreshold,
}

Check failure on line 243 in packages/frontend/src/components/MkNote.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Missing semicolon
})

Check failure on line 244 in packages/frontend/src/components/MkNote.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Missing semicolon
// plugin
if (noteViewInterruptors.length > 0) {
onMounted(async () => {
Expand Down Expand Up @@ -623,6 +632,15 @@
overflow: clip;
contain: content;

.akafav {
font-size: 2rem;
color: red;
}
.aofav {
font-size: 1.5rem;
color: blue;
}

&:focus-visible {
outline: none;

Expand Down
10 changes: 6 additions & 4 deletions packages/frontend/src/components/MkNotes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:ad="true"
:class="$style.notes"
>
<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/>
<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :featured="featured" :withHardMute="true"/>
</MkDateSeparatedList>
</div>
</template>
Expand All @@ -39,11 +39,14 @@ import MkPagination, { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';

const props = defineProps<{
const props = withDefaults(defineProps<{
pagination: Paging;
noGap?: boolean;
disableAutoLoad?: boolean;
}>();
featured: boolean;
}>(), {
featured: false,
});

const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();

Expand All @@ -63,7 +66,6 @@ defineExpose({
&:not(.noGap) {
> .notes {
background: var(--MI_THEME-bg);

.note {
background: var(--MI_THEME-panel);
border-radius: var(--MI-radius);
Expand Down
8 changes: 7 additions & 1 deletion packages/frontend/src/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@

// TODO: instanceをリアクティブにするかは再考の余地あり

export const instance: Misskey.entities.MetaDetailed = reactive(cachedMeta ?? {});

Check failure on line 31 in packages/frontend/src/instance.ts

View workflow job for this annotation

GitHub Actions / lint (frontend)

More than 1 blank line not allowed
type HighlightPopularityThreshold = {
highlightMidPopularityThreshold: number;
highlightHighPopularityThreashold: number;
}

export const instance: Misskey.entities.MetaDetailed & HighlightPopularityThreshold = reactive(cachedMeta ?? {});

export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL);

Expand Down
41 changes: 41 additions & 0 deletions packages/frontend/src/pages/admin/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,38 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</MkButton>
</div>
</MkFolder>
<MkFolder>
<template #icon><i class="ti ti-ghost"></i></template>
<template #label>みつける>ハイライトの調整</template>

<div class="_gaps">
<MkInfo>リアクションしたときに見つける>ハイライトの採用される確率は、大規模サーバーにおいてパフォーマンスへの影響を考慮してデフォルト30%です。(つまり、70%のリアクションはカウントに影響しません)</MkInfo>

<MkKeyValue>
<template #key>ハイライトに採用される確率</template>
<template #value><MkInput v-model.number="infoForm.state.highlightRateFactor" :min="0" :max="100" placeholder="30"/></template>
</MkKeyValue>

<MkKeyValue>
<template #key>ハイライトの青ふぁぼ</template>
<template #value><MkInput v-model.number="infoForm.state.highlightMidPopularityThreshold"/></template>
</MkKeyValue>

<MkKeyValue>
<template #key>ハイライトの赤ふぁぼ</template>
<template #value><MkInput v-model.number="infoForm.state.highlightHighPopularityThreashold"/></template>
</MkKeyValue>

<MkKeyValue>
<template #key>ハイライトのから除外する絵文字</template>
<template #value>
<MkTextarea v-model="infoForm.state.highlightExcludeEmojis" :mfmAutocomplete="['emoji']" :mfmPreview="true"/>
</template>
</MkKeyValue>

<MkFormFooter :form="infoForm"/>
</div>
</MkFolder>
</div>
</MkSpacer>
</MkStickyContainer>
Expand Down Expand Up @@ -290,6 +322,10 @@ const infoForm = useForm({
inquiryUrl: meta.inquiryUrl ?? '',
repositoryUrl: meta.repositoryUrl ?? '',
impressumUrl: meta.impressumUrl ?? '',
highlightRateFactor: meta.highlightRateFactor ?? 30,
highlightMidPopularityThreshold: meta.highlightMidPopularityThreshold ?? 3,
highlightHighPopularityThreashold: meta.highlightHighPopularityThreashold ?? 5,
highlightExcludeEmojis: meta.highlightExcludeEmojis,
}, async (state) => {
await os.apiWithDialog('admin/update-meta', {
name: state.name,
Expand All @@ -302,6 +338,11 @@ const infoForm = useForm({
inquiryUrl: state.inquiryUrl,
repositoryUrl: state.repositoryUrl,
impressumUrl: state.impressumUrl,
highlightRateFactor: state.highlightRateFactor,
highlightMidPopularityThreshold: state.highlightMidPopularityThreshold,
highlightHighPopularityThreashold: state.highlightHighPopularityThreashold ?? 5,
highlightExcludeEmojis: state.highlightExcludeEmojis,

});
fetchInstance(true);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/pages/explore.featured.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="notes">{{ i18n.ts.notes }}</option>
<option value="polls">{{ i18n.ts.poll }}</option>
</MkTab>
<MkNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/>
<MkNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/>
<MkNotes v-if="tab === 'notes'" :featured="true" :pagination="paginationForNotes"/>
<MkNotes v-else-if="tab === 'polls'" :featured="true" :pagination="paginationForPolls"/>
</MkSpacer>
</template>

Expand Down
Loading
Loading