mirror of
https://iceshrimp.dev/crimekillz/iceshrimp-161sh.git
synced 2024-11-24 21:19:07 +01:00
[backend] Rework note hard mutes
It's been shown that the current approach doesn't scale. This implementation should scale perfectly fine.
This commit is contained in:
parent
2d475cb632
commit
ef3463e8dc
@ -203,6 +203,11 @@ reservedUsernames: [
|
|||||||
# prewarm: false
|
# prewarm: false
|
||||||
# dbFallback: false
|
# dbFallback: false
|
||||||
|
|
||||||
|
# Duration hard muted notes are stored in redis for.
|
||||||
|
# Increasing this trades higher memory consumption for lower cpu usage on repeated requests within the specified ttl.
|
||||||
|
#wordMuteCache:
|
||||||
|
# ttl: 24h
|
||||||
|
|
||||||
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
# Congrats, you've reached the end of the config file needed for most deployments!
|
# Congrats, you've reached the end of the config file needed for most deployments!
|
||||||
# Enjoy your Iceshrimp server!
|
# Enjoy your Iceshrimp server!
|
||||||
|
@ -203,6 +203,11 @@ reservedUsernames: [
|
|||||||
# prewarm: false
|
# prewarm: false
|
||||||
# dbFallback: false
|
# dbFallback: false
|
||||||
|
|
||||||
|
# Duration hard muted notes are stored in redis for.
|
||||||
|
# Increasing this trades higher memory consumption for lower cpu usage on repeated requests within the specified ttl.
|
||||||
|
#wordMuteCache:
|
||||||
|
# ttl: 24h
|
||||||
|
|
||||||
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
# Congrats, you've reached the end of the config file needed for most deployments!
|
# Congrats, you've reached the end of the config file needed for most deployments!
|
||||||
# Enjoy your Iceshrimp server!
|
# Enjoy your Iceshrimp server!
|
||||||
|
@ -61,7 +61,13 @@ export default function load() {
|
|||||||
...config.htmlCache,
|
...config.htmlCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.htmlCache.ttlSeconds == null) throw new Error('Failed to parse config.ttl');
|
if (config.htmlCache.ttlSeconds == null) throw new Error('Failed to parse config.htmlCache.ttl');
|
||||||
|
|
||||||
|
config.wordMuteCache = {
|
||||||
|
ttlSeconds: parseDuration(config.wordMuteCache?.ttl ?? '24h', 's')!,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.wordMuteCache.ttlSeconds == null) throw new Error('Failed to parse config.wordMuteCache.ttl');
|
||||||
|
|
||||||
config.searchEngine = config.searchEngine ?? 'https://duckduckgo.com/?q=';
|
config.searchEngine = config.searchEngine ?? 'https://duckduckgo.com/?q=';
|
||||||
|
|
||||||
|
@ -48,6 +48,11 @@ export type Source = {
|
|||||||
dbFallback?: boolean;
|
dbFallback?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wordMuteCache?: {
|
||||||
|
ttl?: string;
|
||||||
|
ttlSeconds?: number;
|
||||||
|
}
|
||||||
|
|
||||||
searchEngine?: string;
|
searchEngine?: string;
|
||||||
|
|
||||||
proxy?: string;
|
proxy?: string;
|
||||||
|
@ -61,7 +61,6 @@ import { Antenna } from "@/models/entities/antenna.js";
|
|||||||
import { PromoNote } from "@/models/entities/promo-note.js";
|
import { PromoNote } from "@/models/entities/promo-note.js";
|
||||||
import { PromoRead } from "@/models/entities/promo-read.js";
|
import { PromoRead } from "@/models/entities/promo-read.js";
|
||||||
import { Relay } from "@/models/entities/relay.js";
|
import { Relay } from "@/models/entities/relay.js";
|
||||||
import { MutedNote } from "@/models/entities/muted-note.js";
|
|
||||||
import { Channel } from "@/models/entities/channel.js";
|
import { Channel } from "@/models/entities/channel.js";
|
||||||
import { ChannelFollowing } from "@/models/entities/channel-following.js";
|
import { ChannelFollowing } from "@/models/entities/channel-following.js";
|
||||||
import { ChannelNotePining } from "@/models/entities/channel-note-pining.js";
|
import { ChannelNotePining } from "@/models/entities/channel-note-pining.js";
|
||||||
@ -168,7 +167,6 @@ export const entities = [
|
|||||||
PromoNote,
|
PromoNote,
|
||||||
PromoRead,
|
PromoRead,
|
||||||
Relay,
|
Relay,
|
||||||
MutedNote,
|
|
||||||
Channel,
|
Channel,
|
||||||
ChannelFollowing,
|
ChannelFollowing,
|
||||||
ChannelNotePining,
|
ChannelNotePining,
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class ReworkHardMutes1701108527387 implements MigrationInterface {
|
||||||
|
name = 'ReworkHardMutes1701108527387'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "muted_note" DROP CONSTRAINT "FK_d8e07aa18c2d64e86201601aec1"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "muted_note" DROP CONSTRAINT "FK_70ab9786313d78e4201d81cdb89"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_a8c6bfd637d3f1d67a27c48e27"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_636e977ff90b23676fb5624b25"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_d8e07aa18c2d64e86201601aec"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_70ab9786313d78e4201d81cdb8"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "muted_note"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."muted_note_reason_enum"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."muted_note_reason_enum" AS ENUM('word', 'manual', 'spam', 'other')`);
|
||||||
|
await queryRunner.query(`CREATE TABLE "muted_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "reason" "public"."muted_note_reason_enum" NOT NULL, CONSTRAINT "PK_897e2eff1c0b9b64e55ca1418a4" PRIMARY KEY ("id")); COMMENT ON COLUMN "muted_note"."noteId" IS 'The note ID.'; COMMENT ON COLUMN "muted_note"."userId" IS 'The user ID.'; COMMENT ON COLUMN "muted_note"."reason" IS 'The reason of the MutedNote.'`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_70ab9786313d78e4201d81cdb8" ON "muted_note" ("noteId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_d8e07aa18c2d64e86201601aec" ON "muted_note" ("userId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_636e977ff90b23676fb5624b25" ON "muted_note" ("reason") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a8c6bfd637d3f1d67a27c48e27" ON "muted_note" ("noteId", "userId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "muted_note" ADD CONSTRAINT "FK_70ab9786313d78e4201d81cdb89" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "muted_note" ADD CONSTRAINT "FK_d8e07aa18c2d64e86201601aec1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import type { User } from "@/models/entities/user.js";
|
import type { User } from "@/models/entities/user.js";
|
||||||
|
import { Packed } from "@/misc/schema.js";
|
||||||
|
|
||||||
type NoteLike = {
|
type NoteLike = {
|
||||||
userId: Note["userId"];
|
userId: Note["userId"];
|
||||||
text: Note["text"];
|
text: Note["text"];
|
||||||
files?: Note["files"];
|
files?: Note["files"];
|
||||||
cw?: Note["cw"];
|
cw?: Note["cw"];
|
||||||
|
reply?: Note["reply"];
|
||||||
|
renote?: Note["renote"];
|
||||||
|
isFiltered?: Packed<"Note">["isFiltered"];
|
||||||
};
|
};
|
||||||
|
|
||||||
type UserLike = {
|
type UserLike = {
|
||||||
@ -14,7 +18,7 @@ type UserLike = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function checkWordMute(
|
function checkWordMute(
|
||||||
note: NoteLike,
|
note: NoteLike | null | undefined,
|
||||||
mutedWords: Array<string | string[]>,
|
mutedWords: Array<string | string[]>,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (note == null) return false;
|
if (note == null) return false;
|
||||||
@ -63,17 +67,13 @@ export async function getWordHardMute(
|
|||||||
mutedWords: Array<string | string[]>,
|
mutedWords: Array<string | string[]>,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
// 自分自身
|
// 自分自身
|
||||||
if (me && note.userId === me.id) {
|
if (me && note.userId === me.id) return false;
|
||||||
return false;
|
if (mutedWords.length <= 0) return false;
|
||||||
}
|
if (note.isFiltered) return true;
|
||||||
|
|
||||||
if (mutedWords.length > 0) {
|
return (
|
||||||
return (
|
checkWordMute(note, mutedWords) ||
|
||||||
checkWordMute(note, mutedWords) ||
|
checkWordMute(note.reply, mutedWords) ||
|
||||||
checkWordMute(note.reply, mutedWords) ||
|
checkWordMute(note.renote, mutedWords)
|
||||||
checkWordMute(note.renote, mutedWords)
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
16
packages/backend/src/misc/is-filtered.ts
Normal file
16
packages/backend/src/misc/is-filtered.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { User } from "@/models/entities/user.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
|
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
||||||
|
import { Cache } from "@/misc/cache.js";
|
||||||
|
import { unique } from "@/prelude/array.js";
|
||||||
|
import config from "@/config/index.js";
|
||||||
|
|
||||||
|
const filteredNoteCache = new Cache<boolean>("filteredNote", config.wordMuteCache?.ttlSeconds ?? 60 * 60 * 24);
|
||||||
|
|
||||||
|
export function isFiltered(note: Note, user: { id: User["id"] } | null | undefined, profile: { mutedWords: UserProfile["mutedWords"] } | null): boolean | Promise<boolean> {
|
||||||
|
if (!user || !profile) return false;
|
||||||
|
if (profile.mutedWords.length < 1) return false;
|
||||||
|
return filteredNoteCache.fetch(`${note.id}:${(note.updatedAt ?? note.createdAt).getTime()}:${user.id}`,
|
||||||
|
() => getWordHardMute(note, user, unique(profile.mutedWords)));
|
||||||
|
}
|
@ -520,7 +520,7 @@ export class Meta {
|
|||||||
public donationLink: string | null;
|
public donationLink: string | null;
|
||||||
|
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: 64,
|
length: 128,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public autofollowedAccount: string | null;
|
public autofollowedAccount: string | null;
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import {
|
|
||||||
Entity,
|
|
||||||
Index,
|
|
||||||
JoinColumn,
|
|
||||||
Column,
|
|
||||||
ManyToOne,
|
|
||||||
PrimaryColumn,
|
|
||||||
} from "typeorm";
|
|
||||||
import { Note } from "./note.js";
|
|
||||||
import { User } from "./user.js";
|
|
||||||
import { id } from "../id.js";
|
|
||||||
import { mutedNoteReasons } from "../../types.js";
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
@Index(["noteId", "userId"], { unique: true })
|
|
||||||
export class MutedNote {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: "The note ID.",
|
|
||||||
})
|
|
||||||
public noteId: Note["id"];
|
|
||||||
|
|
||||||
@ManyToOne((type) => Note, {
|
|
||||||
onDelete: "CASCADE",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public note: Note | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: "The user ID.",
|
|
||||||
})
|
|
||||||
public userId: User["id"];
|
|
||||||
|
|
||||||
@ManyToOne((type) => User, {
|
|
||||||
onDelete: "CASCADE",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ミュートされた理由。
|
|
||||||
*/
|
|
||||||
@Index()
|
|
||||||
@Column("enum", {
|
|
||||||
enum: mutedNoteReasons,
|
|
||||||
comment: "The reason of the MutedNote.",
|
|
||||||
})
|
|
||||||
public reason: typeof mutedNoteReasons[number];
|
|
||||||
}
|
|
@ -259,7 +259,7 @@ export class Note {
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
comment: "The updated date of the Note.",
|
comment: "The updated date of the Note.",
|
||||||
})
|
})
|
||||||
public updatedAt: Date;
|
public updatedAt: Date | null;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
constructor(data: Partial<Note>) {
|
constructor(data: Partial<Note>) {
|
||||||
|
@ -56,7 +56,6 @@ import { PromoRead } from "./entities/promo-read.js";
|
|||||||
import { EmojiRepository } from "./repositories/emoji.js";
|
import { EmojiRepository } from "./repositories/emoji.js";
|
||||||
import { RelayRepository } from "./repositories/relay.js";
|
import { RelayRepository } from "./repositories/relay.js";
|
||||||
import { ChannelRepository } from "./repositories/channel.js";
|
import { ChannelRepository } from "./repositories/channel.js";
|
||||||
import { MutedNote } from "./entities/muted-note.js";
|
|
||||||
import { ChannelFollowing } from "./entities/channel-following.js";
|
import { ChannelFollowing } from "./entities/channel-following.js";
|
||||||
import { ChannelNotePining } from "./entities/channel-note-pining.js";
|
import { ChannelNotePining } from "./entities/channel-note-pining.js";
|
||||||
import { RegistryItem } from "./entities/registry-item.js";
|
import { RegistryItem } from "./entities/registry-item.js";
|
||||||
@ -129,7 +128,6 @@ export const Antennas = AntennaRepository;
|
|||||||
export const PromoNotes = db.getRepository(PromoNote);
|
export const PromoNotes = db.getRepository(PromoNote);
|
||||||
export const PromoReads = db.getRepository(PromoRead);
|
export const PromoReads = db.getRepository(PromoRead);
|
||||||
export const Relays = RelayRepository;
|
export const Relays = RelayRepository;
|
||||||
export const MutedNotes = db.getRepository(MutedNote);
|
|
||||||
export const Channels = ChannelRepository;
|
export const Channels = ChannelRepository;
|
||||||
export const ChannelFollowings = db.getRepository(ChannelFollowing);
|
export const ChannelFollowings = db.getRepository(ChannelFollowing);
|
||||||
export const ChannelNotePinings = db.getRepository(ChannelNotePining);
|
export const ChannelNotePinings = db.getRepository(ChannelNotePining);
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
Followings,
|
Followings,
|
||||||
Polls,
|
Polls,
|
||||||
Channels,
|
Channels,
|
||||||
Notes,
|
Notes, UserProfiles,
|
||||||
} from "../index.js";
|
} from "../index.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
import { nyaize } from "@/misc/nyaize.js";
|
import { nyaize } from "@/misc/nyaize.js";
|
||||||
@ -29,6 +29,11 @@ import {
|
|||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||||
import { PackedUserCache } from "@/models/repositories/user.js";
|
import { PackedUserCache } from "@/models/repositories/user.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
|
import { Cache } from "@/misc/cache.js";
|
||||||
|
|
||||||
|
const mutedWordsCache = new Cache<UserProfile["mutedWords"]>("mutedWords", 60 * 5);
|
||||||
|
|
||||||
export async function populatePoll(note: Note, meId: User["id"] | null) {
|
export async function populatePoll(note: Note, meId: User["id"] | null) {
|
||||||
const poll = await Polls.findOneByOrFail({ noteId: note.id });
|
const poll = await Polls.findOneByOrFail({ noteId: note.id });
|
||||||
@ -175,6 +180,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
userCache: PackedUserCache = Users.getFreshPackedUserCache(),
|
userCache: PackedUserCache = Users.getFreshPackedUserCache(),
|
||||||
|
profile?: { mutedWords: UserProfile["mutedWords"] } | null,
|
||||||
): Promise<Packed<"Note">> {
|
): Promise<Packed<"Note">> {
|
||||||
const opts = Object.assign(
|
const opts = Object.assign(
|
||||||
{
|
{
|
||||||
@ -187,6 +193,11 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||||||
const note =
|
const note =
|
||||||
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
||||||
const host = note.userHost;
|
const host = note.userHost;
|
||||||
|
const meProfile = profile !== undefined
|
||||||
|
? profile
|
||||||
|
: meId !== null
|
||||||
|
? { mutedWords: await mutedWordsCache.fetch(meId, async () => UserProfiles.findOneBy({ userId: meId }).then(p => p?.mutedWords ?? [])) }
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!(await this.isVisibleForMe(note, meId))) {
|
if (!(await this.isVisibleForMe(note, meId))) {
|
||||||
throw new IdentifiableError(
|
throw new IdentifiableError(
|
||||||
@ -257,7 +268,8 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||||||
...(meId
|
...(meId
|
||||||
? {
|
? {
|
||||||
myReaction: populateMyReaction(note, meId, options?._hint_),
|
myReaction: populateMyReaction(note, meId, options?._hint_),
|
||||||
isRenoted: populateIsRenoted(note, meId, options?._hint_)
|
isRenoted: populateIsRenoted(note, meId, options?._hint_),
|
||||||
|
isFiltered: isFiltered(note, me, await meProfile),
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|
||||||
@ -326,6 +338,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||||||
options?: {
|
options?: {
|
||||||
detail?: boolean;
|
detail?: boolean;
|
||||||
},
|
},
|
||||||
|
profile?: { mutedWords: UserProfile["mutedWords"] } | null,
|
||||||
) {
|
) {
|
||||||
if (notes.length === 0) return [];
|
if (notes.length === 0) return [];
|
||||||
|
|
||||||
@ -361,6 +374,10 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||||||
!!myRenotes.find(p => p.renoteId == target),
|
!!myRenotes.find(p => p.renoteId == target),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile = profile !== undefined
|
||||||
|
? profile
|
||||||
|
: { mutedWords: await mutedWordsCache.fetch(meId, async () => UserProfiles.findOneBy({ userId: meId }).then(p => p?.mutedWords ?? [])) };
|
||||||
}
|
}
|
||||||
|
|
||||||
await prefetchEmojis(aggregateNoteEmojis(notes));
|
await prefetchEmojis(aggregateNoteEmojis(notes));
|
||||||
@ -373,7 +390,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||||||
myReactions: myReactionsMap,
|
myReactions: myReactionsMap,
|
||||||
myRenotes: myRenotesMap
|
myRenotes: myRenotesMap
|
||||||
},
|
},
|
||||||
}),
|
}, undefined, profile),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -199,6 +199,11 @@ export const packedNoteSchema = {
|
|||||||
type: "boolean",
|
type: "boolean",
|
||||||
optional: true,
|
optional: true,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
}
|
},
|
||||||
|
isFiltered: {
|
||||||
|
type: "boolean",
|
||||||
|
optional: true,
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import type { User } from "@/models/entities/user.js";
|
|
||||||
import { MutedNotes } from "@/models/index.js";
|
|
||||||
import type { SelectQueryBuilder } from "typeorm";
|
|
||||||
|
|
||||||
export function generateMutedNoteQuery(
|
|
||||||
q: SelectQueryBuilder<any>,
|
|
||||||
me: { id: User["id"] },
|
|
||||||
) {
|
|
||||||
const mutedQuery = MutedNotes.createQueryBuilder("muted")
|
|
||||||
.select("muted.noteId")
|
|
||||||
.where("muted.userId = :userId", { userId: me.id });
|
|
||||||
|
|
||||||
q.andWhere(`note.id NOT IN (${mutedQuery.getQuery()})`);
|
|
||||||
|
|
||||||
q.setParameters(mutedQuery.getParameters());
|
|
||||||
}
|
|
@ -186,7 +186,6 @@ import * as ep___i_exportUserLists from "./endpoints/i/export-user-lists.js";
|
|||||||
import * as ep___i_favorites from "./endpoints/i/favorites.js";
|
import * as ep___i_favorites from "./endpoints/i/favorites.js";
|
||||||
import * as ep___i_gallery_likes from "./endpoints/i/gallery/likes.js";
|
import * as ep___i_gallery_likes from "./endpoints/i/gallery/likes.js";
|
||||||
import * as ep___i_gallery_posts from "./endpoints/i/gallery/posts.js";
|
import * as ep___i_gallery_posts from "./endpoints/i/gallery/posts.js";
|
||||||
import * as ep___i_getWordMutedNotesCount from "./endpoints/i/get-word-muted-notes-count.js";
|
|
||||||
import * as ep___i_importBlocking from "./endpoints/i/import-blocking.js";
|
import * as ep___i_importBlocking from "./endpoints/i/import-blocking.js";
|
||||||
import * as ep___i_importFollowing from "./endpoints/i/import-following.js";
|
import * as ep___i_importFollowing from "./endpoints/i/import-following.js";
|
||||||
import * as ep___i_importMuting from "./endpoints/i/import-muting.js";
|
import * as ep___i_importMuting from "./endpoints/i/import-muting.js";
|
||||||
@ -535,7 +534,6 @@ const eps = [
|
|||||||
["i/favorites", ep___i_favorites],
|
["i/favorites", ep___i_favorites],
|
||||||
["i/gallery/likes", ep___i_gallery_likes],
|
["i/gallery/likes", ep___i_gallery_likes],
|
||||||
["i/gallery/posts", ep___i_gallery_posts],
|
["i/gallery/posts", ep___i_gallery_posts],
|
||||||
["i/get-word-muted-notes-count", ep___i_getWordMutedNotesCount],
|
|
||||||
["i/import-blocking", ep___i_importBlocking],
|
["i/import-blocking", ep___i_importBlocking],
|
||||||
["i/import-following", ep___i_importFollowing],
|
["i/import-following", ep___i_importFollowing],
|
||||||
["i/import-muting", ep___i_importMuting],
|
["i/import-muting", ep___i_importMuting],
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
import define from "../../define.js";
|
|
||||||
import { MutedNotes } from "@/models/index.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["account"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "read:account",
|
|
||||||
|
|
||||||
res: {
|
|
||||||
type: "object",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
properties: {
|
|
||||||
count: {
|
|
||||||
type: "number",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {},
|
|
||||||
required: [],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
return {
|
|
||||||
count: await MutedNotes.countBy({
|
|
||||||
userId: user.id,
|
|
||||||
reason: "word",
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
|
@ -6,7 +6,6 @@ import { ApiError } from "../../error.js";
|
|||||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
|
|
||||||
@ -89,7 +88,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
generateRepliesQuery(query, ps.withReplies, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
if (user) {
|
if (user) {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
generateMutedUserRenotesQueryForNotes(query, user);
|
generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
|||||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
||||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
@ -108,7 +107,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
generateRepliesQuery(query, ps.withReplies, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
generateMutedUserRenotesQueryForNotes(query, user);
|
generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ import define from "../../define.js";
|
|||||||
import { ApiError } from "../../error.js";
|
import { ApiError } from "../../error.js";
|
||||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
||||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
@ -99,7 +97,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
generateChannelQuery(query, user);
|
generateChannelQuery(query, user);
|
||||||
generateRepliesQuery(query, ps.withReplies, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
if (user) generateMutedUserQuery(query, user);
|
if (user) generateMutedUserQuery(query, user);
|
||||||
if (user) generateMutedNoteQuery(query, user);
|
|
||||||
if (user) generateBlockedUserQuery(query, user);
|
if (user) generateBlockedUserQuery(query, user);
|
||||||
if (user) generateMutedUserRenotesQueryForNotes(query, user);
|
if (user) generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ import define from "../../define.js";
|
|||||||
import { ApiError } from "../../error.js";
|
import { ApiError } from "../../error.js";
|
||||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
||||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
@ -99,7 +97,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
generateChannelQuery(query, user);
|
generateChannelQuery(query, user);
|
||||||
generateRepliesQuery(query, ps.withReplies, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
if (user) generateMutedUserQuery(query, user);
|
if (user) generateMutedUserQuery(query, user);
|
||||||
if (user) generateMutedNoteQuery(query, user);
|
|
||||||
if (user) generateBlockedUserQuery(query, user);
|
if (user) generateBlockedUserQuery(query, user);
|
||||||
if (user) generateMutedUserRenotesQueryForNotes(query, user);
|
if (user) generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
|||||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
||||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js";
|
||||||
@ -86,7 +85,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
generateRepliesQuery(query, ps.withReplies, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
generateMutedUserRenotesQueryForNotes(query, user);
|
generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ export class NoteConverter {
|
|||||||
reactions: populated.then(populated => Promise.resolve(reaction).then(reaction => this.encodeReactions(note.reactions, reaction?.reaction, populated))),
|
reactions: populated.then(populated => Promise.resolve(reaction).then(reaction => this.encodeReactions(note.reactions, reaction?.reaction, populated))),
|
||||||
bookmarked: isBookmarked,
|
bookmarked: isBookmarked,
|
||||||
quote: reblog.then(reblog => isQuote(note) ? reblog : null),
|
quote: reblog.then(reblog => isQuote(note) ? reblog : null),
|
||||||
edited_at: note.updatedAt?.toISOString()
|
edited_at: note.updatedAt?.toISOString() ?? null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ export class NoteConverter {
|
|||||||
.then(p => p ?? escapeMFM(text))
|
.then(p => p ?? escapeMFM(text))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
HtmlNoteCacheEntries.upsert({ noteId: note.id, updatedAt: note.updatedAt, content: await content }, ["noteId"]);
|
HtmlNoteCacheEntries.upsert({ noteId: note.id, updatedAt: note.updatedAt ?? note.createdAt, content: await content }, ["noteId"]);
|
||||||
await this.noteContentHtmlCache.set(identifier, await content);
|
await this.noteContentHtmlCache.set(identifier, await content);
|
||||||
return { content } as HtmlNoteCacheEntry;
|
return { content } as HtmlNoteCacheEntry;
|
||||||
});
|
});
|
||||||
@ -367,6 +367,6 @@ export class NoteConverter {
|
|||||||
this.noteContentHtmlCache.set(identifier, await content);
|
this.noteContentHtmlCache.set(identifier, await content);
|
||||||
|
|
||||||
if (config.htmlCache?.dbFallback)
|
if (config.htmlCache?.dbFallback)
|
||||||
HtmlNoteCacheEntries.upsert({ noteId: note.id, updatedAt: note.updatedAt, content: await content }, ["noteId"]);
|
HtmlNoteCacheEntries.upsert({ noteId: note.id, updatedAt: note.updatedAt ?? note.createdAt, content: await content }, ["noteId"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { generateChannelQuery } from "@/server/api/common/generate-channel-query
|
|||||||
import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js";
|
import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js";
|
||||||
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
||||||
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
||||||
import { generateMutedNoteQuery } from "@/server/api/common/generate-muted-note-query.js";
|
|
||||||
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
||||||
import { generateMutedUserRenotesQueryForNotes } from "@/server/api/common/generated-muted-renote-query.js";
|
import { generateMutedUserRenotesQueryForNotes } from "@/server/api/common/generated-muted-renote-query.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
@ -43,7 +42,6 @@ export class TimelineHelpers {
|
|||||||
generateRepliesQuery(query, true, user);
|
generateRepliesQuery(query, true, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
generateMutedUserRenotesQueryForNotes(query, user);
|
generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
|
|
||||||
@ -88,7 +86,6 @@ export class TimelineHelpers {
|
|||||||
generateRepliesQuery(query, true, user);
|
generateRepliesQuery(query, true, user);
|
||||||
if (user) {
|
if (user) {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
generateMutedUserRenotesQueryForNotes(query, user);
|
generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
}
|
}
|
||||||
@ -158,7 +155,6 @@ export class TimelineHelpers {
|
|||||||
generateRepliesQuery(query, true, user);
|
generateRepliesQuery(query, true, user);
|
||||||
if (user) {
|
if (user) {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
generateMutedUserRenotesQueryForNotes(query, user);
|
generateMutedUserRenotesQueryForNotes(query, user);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { MastodonStream } from "../channel.js";
|
import { MastodonStream } from "../channel.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||||
import { StreamMessages } from "@/server/api/stream/types.js";
|
import { StreamMessages } from "@/server/api/stream/types.js";
|
||||||
import { Packed } from "@/misc/schema.js";
|
import { Packed } from "@/misc/schema.js";
|
||||||
import { User } from "@/models/entities/user.js";
|
import { User } from "@/models/entities/user.js";
|
||||||
import { UserListJoinings } from "@/models/index.js";
|
import { UserListJoinings } from "@/models/index.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
|
||||||
export class MastodonStreamList extends MastodonStream {
|
export class MastodonStreamList extends MastodonStream {
|
||||||
public static shouldShare = false;
|
public static shouldShare = false;
|
||||||
@ -75,7 +75,7 @@ export class MastodonStreamList extends MastodonStream {
|
|||||||
if (!this.listUsers.includes(note.userId)) return false;
|
if (!this.listUsers.includes(note.userId)) return false;
|
||||||
if (note.channelId) return false;
|
if (note.channelId) return false;
|
||||||
if (note.renoteId !== null && !note.text && this.renoteMuting.has(note.userId)) return false;
|
if (note.renoteId !== null && !note.text && this.renoteMuting.has(note.userId)) return false;
|
||||||
if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false;
|
if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false;
|
||||||
if (note.visibility === "specified") return !!note.visibleUserIds?.includes(this.user.id);
|
if (note.visibility === "specified") return !!note.visibleUserIds?.includes(this.user.id);
|
||||||
if (note.visibility === "followers") return this.following.has(note.userId);
|
if (note.visibility === "followers") return this.following.has(note.userId);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { MastodonStream } from "../channel.js";
|
import { MastodonStream } from "../channel.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
@ -7,6 +6,7 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
|||||||
import { StreamMessages } from "@/server/api/stream/types.js";
|
import { StreamMessages } from "@/server/api/stream/types.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import isQuote from "@/misc/is-quote.js";
|
import isQuote from "@/misc/is-quote.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
|
||||||
export class MastodonStreamPublic extends MastodonStream {
|
export class MastodonStreamPublic extends MastodonStream {
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
@ -72,7 +72,7 @@ export class MastodonStreamPublic extends MastodonStream {
|
|||||||
if (isUserRelated(note, this.muting)) return false;
|
if (isUserRelated(note, this.muting)) return false;
|
||||||
if (isUserRelated(note, this.blocking)) return false;
|
if (isUserRelated(note, this.blocking)) return false;
|
||||||
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
||||||
if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false;
|
if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { MastodonStream } from "../channel.js";
|
import { MastodonStream } from "../channel.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||||
import { StreamMessages } from "@/server/api/stream/types.js";
|
import { StreamMessages } from "@/server/api/stream/types.js";
|
||||||
import isQuote from "@/misc/is-quote.js";
|
import isQuote from "@/misc/is-quote.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
|
||||||
export class MastodonStreamTag extends MastodonStream {
|
export class MastodonStreamTag extends MastodonStream {
|
||||||
public static shouldShare = false;
|
public static shouldShare = false;
|
||||||
@ -64,7 +64,7 @@ export class MastodonStreamTag extends MastodonStream {
|
|||||||
if (isUserRelated(note, this.muting)) return false;
|
if (isUserRelated(note, this.muting)) return false;
|
||||||
if (isUserRelated(note, this.blocking)) return false;
|
if (isUserRelated(note, this.blocking)) return false;
|
||||||
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
||||||
if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false;
|
if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { MastodonStream } from "../channel.js";
|
import { MastodonStream } from "../channel.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
@ -8,6 +7,7 @@ import { StreamMessages } from "@/server/api/stream/types.js";
|
|||||||
import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js";
|
import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js";
|
||||||
import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js";
|
import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js";
|
||||||
import isQuote from "@/misc/is-quote.js";
|
import isQuote from "@/misc/is-quote.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
|
||||||
export class MastodonStreamUser extends MastodonStream {
|
export class MastodonStreamUser extends MastodonStream {
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
@ -99,7 +99,7 @@ export class MastodonStreamUser extends MastodonStream {
|
|||||||
if (isUserRelated(note, this.blocking)) return false;
|
if (isUserRelated(note, this.blocking)) return false;
|
||||||
if (isUserRelated(note, this.hidden)) return false;
|
if (isUserRelated(note, this.hidden)) return false;
|
||||||
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
||||||
if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false;
|
if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import Channel from "../channel.js";
|
import Channel from "../channel.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
public readonly chName = "globalTimeline";
|
public readonly chName = "globalTimeline";
|
||||||
@ -68,7 +69,7 @@ export default class extends Channel {
|
|||||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||||
if (
|
if (
|
||||||
this.userProfile &&
|
this.userProfile &&
|
||||||
(await getWordHardMute(note, this.user, this.userProfile.mutedWords))
|
(await isFiltered(note as unknown as Note, this.user, this.userProfile))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import Channel from "../channel.js";
|
import Channel from "../channel.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
public readonly chName = "homeTimeline";
|
public readonly chName = "homeTimeline";
|
||||||
@ -69,7 +70,7 @@ export default class extends Channel {
|
|||||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||||
if (
|
if (
|
||||||
this.userProfile &&
|
this.userProfile &&
|
||||||
(await getWordHardMute(note, this.user, this.userProfile.mutedWords))
|
(await isFiltered(note as unknown as Note, this.user, this.userProfile))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import Channel from "../channel.js";
|
import Channel from "../channel.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
public readonly chName = "hybridTimeline";
|
public readonly chName = "hybridTimeline";
|
||||||
@ -86,7 +87,7 @@ export default class extends Channel {
|
|||||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||||
if (
|
if (
|
||||||
this.userProfile &&
|
this.userProfile &&
|
||||||
(await getWordHardMute(note, this.user, this.userProfile.mutedWords))
|
(await isFiltered(note as unknown as Note, this.user, this.userProfile))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import Channel from "../channel.js";
|
import Channel from "../channel.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
public readonly chName = "localTimeline";
|
public readonly chName = "localTimeline";
|
||||||
@ -60,7 +61,7 @@ export default class extends Channel {
|
|||||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||||
if (
|
if (
|
||||||
this.userProfile &&
|
this.userProfile &&
|
||||||
(await getWordHardMute(note, this.user, this.userProfile.mutedWords))
|
(await isFiltered(note as unknown as Note, this.user, this.userProfile))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import Channel from "../channel.js";
|
import Channel from "../channel.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { isUserRelated } from "@/misc/is-user-related.js";
|
import { isUserRelated } from "@/misc/is-user-related.js";
|
||||||
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
import { isInstanceMuted } from "@/misc/is-instance-muted.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
|
import { isFiltered } from "@/misc/is-filtered.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
public readonly chName = "recommendedTimeline";
|
public readonly chName = "recommendedTimeline";
|
||||||
@ -82,7 +83,7 @@ export default class extends Channel {
|
|||||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||||
if (
|
if (
|
||||||
this.userProfile &&
|
this.userProfile &&
|
||||||
(await getWordHardMute(note, this.user, this.userProfile.mutedWords))
|
(await isFiltered(note as unknown as Note, this.user, this.userProfile))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ import {
|
|||||||
Notes,
|
Notes,
|
||||||
Instances,
|
Instances,
|
||||||
UserProfiles,
|
UserProfiles,
|
||||||
MutedNotes,
|
|
||||||
Channels,
|
Channels,
|
||||||
ChannelFollowings,
|
ChannelFollowings,
|
||||||
NoteThreadMutings,
|
NoteThreadMutings,
|
||||||
@ -48,7 +47,6 @@ import { Poll } from "@/models/entities/poll.js";
|
|||||||
import { createNotification } from "../create-notification.js";
|
import { createNotification } from "../create-notification.js";
|
||||||
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
|
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
|
||||||
import { checkHitAntenna } from "@/misc/check-hit-antenna.js";
|
import { checkHitAntenna } from "@/misc/check-hit-antenna.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.js";
|
|
||||||
import { addNoteToAntenna } from "../add-note-to-antenna.js";
|
import { addNoteToAntenna } from "../add-note-to-antenna.js";
|
||||||
import { countSameRenotes } from "@/misc/count-same-renotes.js";
|
import { countSameRenotes } from "@/misc/count-same-renotes.js";
|
||||||
import { deliverToRelays, getCachedRelays } from "../relay.js";
|
import { deliverToRelays, getCachedRelays } from "../relay.js";
|
||||||
@ -57,8 +55,6 @@ import { normalizeForSearch } from "@/misc/normalize-for-search.js";
|
|||||||
import { getAntennas } from "@/misc/antenna-cache.js";
|
import { getAntennas } from "@/misc/antenna-cache.js";
|
||||||
import { endedPollNotificationQueue } from "@/queue/queues.js";
|
import { endedPollNotificationQueue } from "@/queue/queues.js";
|
||||||
import { webhookDeliver } from "@/queue/index.js";
|
import { webhookDeliver } from "@/queue/index.js";
|
||||||
import { Cache } from "@/misc/cache.js";
|
|
||||||
import type { UserProfile } from "@/models/entities/user-profile.js";
|
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
import { getActiveWebhooks } from "@/misc/webhook-cache.js";
|
import { getActiveWebhooks } from "@/misc/webhook-cache.js";
|
||||||
import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
|
import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
|
||||||
@ -67,10 +63,6 @@ import { Mutex } from "redis-semaphore";
|
|||||||
import { RecursionLimiter } from "@/models/repositories/user-profile.js";
|
import { RecursionLimiter } from "@/models/repositories/user-profile.js";
|
||||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||||
|
|
||||||
const mutedWordsCache = new Cache<
|
|
||||||
{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]
|
|
||||||
>("mutedWords", 60 * 5);
|
|
||||||
|
|
||||||
type NotificationType = "reply" | "renote" | "quote" | "mention";
|
type NotificationType = "reply" | "renote" | "quote" | "mention";
|
||||||
|
|
||||||
class NotificationManager {
|
class NotificationManager {
|
||||||
@ -367,33 +359,6 @@ export default async (
|
|||||||
// Increment notes count (user)
|
// Increment notes count (user)
|
||||||
incNotesCountOfUser(user);
|
incNotesCountOfUser(user);
|
||||||
|
|
||||||
// Word mute
|
|
||||||
mutedWordsCache
|
|
||||||
.fetch(null, () =>
|
|
||||||
UserProfiles.find({
|
|
||||||
where: {
|
|
||||||
enableWordMute: true,
|
|
||||||
},
|
|
||||||
select: ["userId", "mutedWords"],
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.then((us) => {
|
|
||||||
for (const u of us) {
|
|
||||||
getWordHardMute(data, { id: u.userId }, u.mutedWords).then(
|
|
||||||
(shouldMute) => {
|
|
||||||
if (shouldMute) {
|
|
||||||
MutedNotes.insert({
|
|
||||||
id: genId(),
|
|
||||||
userId: u.userId,
|
|
||||||
noteId: note.id,
|
|
||||||
reason: "word",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Antenna
|
// Antenna
|
||||||
for (const antenna of await getAntennas()) {
|
for (const antenna of await getAntennas()) {
|
||||||
checkHitAntenna(antenna, note, user).then((hit) => {
|
checkHitAntenna(antenna, note, user).then((hit) => {
|
||||||
|
@ -21,6 +21,4 @@ export const noteVisibilities = [
|
|||||||
"hidden",
|
"hidden",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const mutedNoteReasons = ["word", "manual", "spam", "other"] as const;
|
|
||||||
|
|
||||||
export const ffVisibility = ["public", "followers", "private"] as const;
|
export const ffVisibility = ["public", "followers", "private"] as const;
|
||||||
|
@ -8,7 +8,7 @@ export default defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
items: {
|
items: {
|
||||||
type: Array as PropType<
|
type: Array as PropType<
|
||||||
{ id: string; createdAt: string; _shouldInsertAd_: boolean }[]
|
{ id: string; createdAt: string; }[]
|
||||||
>,
|
>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
@ -162,17 +162,13 @@ const init = async (): Promise<void> => {
|
|||||||
redisPaginationStr = res.pagination;
|
redisPaginationStr = res.pagination;
|
||||||
res = res.notes;
|
res = res.notes;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < res.length; i++) {
|
|
||||||
const item = res[i];
|
const length = res.length;
|
||||||
if (props.pagination.reversed) {
|
res = (res as Item[]).filter(p => !p.isFiltered);
|
||||||
if (i === res.length - 2) item._shouldInsertAd_ = true;
|
|
||||||
} else {
|
|
||||||
if (i === 3) item._shouldInsertAd_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
!props.pagination.noPaging &&
|
!props.pagination.noPaging &&
|
||||||
res.length > (props.pagination.limit || 10)
|
length > (props.pagination.limit || 10)
|
||||||
) {
|
) {
|
||||||
res.pop();
|
res.pop();
|
||||||
items.value = props.pagination.reversed
|
items.value = props.pagination.reversed
|
||||||
@ -201,7 +197,7 @@ const reload = (): void => {
|
|||||||
init();
|
init();
|
||||||
};
|
};
|
||||||
|
|
||||||
const refresh = async (): void => {
|
const refresh = async (): Promise<void> => {
|
||||||
const params = props.pagination.params
|
const params = props.pagination.params
|
||||||
? isRef(props.pagination.params)
|
? isRef(props.pagination.params)
|
||||||
? props.pagination.params.value
|
? props.pagination.params.value
|
||||||
@ -290,15 +286,10 @@ const fetchMore = async (): Promise<void> => {
|
|||||||
res = res.notes;
|
res = res.notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < res.length; i++) {
|
const length = res.length;
|
||||||
const item = res[i];
|
res = (res as Item[]).filter(p => !p.isFiltered);
|
||||||
if (props.pagination.reversed) {
|
|
||||||
if (i === res.length - 9) item._shouldInsertAd_ = true;
|
if (length > SECOND_FETCH_LIMIT) {
|
||||||
} else {
|
|
||||||
if (i === 10) item._shouldInsertAd_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (res.length > SECOND_FETCH_LIMIT) {
|
|
||||||
res.pop();
|
res.pop();
|
||||||
items.value = props.pagination.reversed
|
items.value = props.pagination.reversed
|
||||||
? [...res].reverse().concat(items.value)
|
? [...res].reverse().concat(items.value)
|
||||||
@ -361,7 +352,9 @@ const fetchMoreAhead = async (): Promise<void> => {
|
|||||||
res = res.notes;
|
res = res.notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.length > SECOND_FETCH_LIMIT) {
|
const length = res.length;
|
||||||
|
res = (res as Item[]).filter(p => !p.isFiltered);
|
||||||
|
if (length > SECOND_FETCH_LIMIT) {
|
||||||
res.pop();
|
res.pop();
|
||||||
items.value = props.pagination.reversed
|
items.value = props.pagination.reversed
|
||||||
? [...res].reverse().concat(items.value)
|
? [...res].reverse().concat(items.value)
|
||||||
@ -383,6 +376,7 @@ const fetchMoreAhead = async (): Promise<void> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const prepend = (item: Item): void => {
|
const prepend = (item: Item): void => {
|
||||||
|
if (item.isFiltered) return;
|
||||||
if (props.pagination.reversed) {
|
if (props.pagination.reversed) {
|
||||||
if (rootEl.value) {
|
if (rootEl.value) {
|
||||||
const container = getScrollContainer(rootEl.value);
|
const container = getScrollContainer(rootEl.value);
|
||||||
@ -446,6 +440,7 @@ const prepend = (item: Item): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const append = (item: Item): void => {
|
const append = (item: Item): void => {
|
||||||
|
if (item.isFiltered) return;
|
||||||
items.value.push(item);
|
items.value.push(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,15 +31,6 @@
|
|||||||
}}</template
|
}}</template
|
||||||
>
|
>
|
||||||
</FormTextarea>
|
</FormTextarea>
|
||||||
<MkKeyValue
|
|
||||||
v-if="hardWordMutedNotesCount != null"
|
|
||||||
class="_formBlock"
|
|
||||||
>
|
|
||||||
<template #key>{{ i18n.ts._wordMute.mutedNotes }}</template>
|
|
||||||
<template #value>{{
|
|
||||||
number(hardWordMutedNotesCount)
|
|
||||||
}}</template>
|
|
||||||
</MkKeyValue>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton primary inline :disabled="!changed" @click="save()"
|
<MkButton primary inline :disabled="!changed" @click="save()"
|
||||||
@ -77,13 +68,8 @@ const render = (mutedWords) =>
|
|||||||
const tab = ref("soft");
|
const tab = ref("soft");
|
||||||
const softMutedWords = ref(render(defaultStore.state.mutedWords));
|
const softMutedWords = ref(render(defaultStore.state.mutedWords));
|
||||||
const hardMutedWords = ref(render($i!.mutedWords));
|
const hardMutedWords = ref(render($i!.mutedWords));
|
||||||
const hardWordMutedNotesCount = ref(null);
|
|
||||||
const changed = ref(false);
|
const changed = ref(false);
|
||||||
|
|
||||||
os.api("i/get-word-muted-notes-count", {}).then((response) => {
|
|
||||||
hardWordMutedNotesCount.value = response?.count;
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(softMutedWords, () => {
|
watch(softMutedWords, () => {
|
||||||
changed.value = true;
|
changed.value = true;
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [ffVisibility](./iceshrimp-js.ffvisibility.md) | |
|
| [ffVisibility](./iceshrimp-js.ffvisibility.md) | |
|
||||||
| [mutedNoteReasons](./iceshrimp-js.mutednotereasons.md) | |
|
|
||||||
| [noteVisibilities](./iceshrimp-js.notevisibilities.md) | |
|
| [noteVisibilities](./iceshrimp-js.notevisibilities.md) | |
|
||||||
| [notificationTypes](./iceshrimp-js.notificationtypes.md) | |
|
| [notificationTypes](./iceshrimp-js.notificationtypes.md) | |
|
||||||
| [permissions](./iceshrimp-js.permissions.md) | |
|
| [permissions](./iceshrimp-js.permissions.md) | |
|
||||||
|
@ -90,10 +90,6 @@ export type Endpoints = {
|
|||||||
"admin/update-meta": { req: TODO; res: TODO };
|
"admin/update-meta": { req: TODO; res: TODO };
|
||||||
"admin/vacuum": { req: TODO; res: TODO };
|
"admin/vacuum": { req: TODO; res: TODO };
|
||||||
"admin/accounts/create": { req: TODO; res: TODO };
|
"admin/accounts/create": { req: TODO; res: TODO };
|
||||||
"admin/ad/create": { req: TODO; res: TODO };
|
|
||||||
"admin/ad/delete": { req: { id: Ad["id"] }; res: null };
|
|
||||||
"admin/ad/list": { req: TODO; res: TODO };
|
|
||||||
"admin/ad/update": { req: TODO; res: TODO };
|
|
||||||
"admin/announcements/create": { req: TODO; res: TODO };
|
"admin/announcements/create": { req: TODO; res: TODO };
|
||||||
"admin/announcements/delete": { req: { id: Announcement["id"] }; res: null };
|
"admin/announcements/delete": { req: { id: Announcement["id"] }; res: null };
|
||||||
"admin/announcements/list": { req: TODO; res: TODO };
|
"admin/announcements/list": { req: TODO; res: TODO };
|
||||||
@ -630,7 +626,6 @@ export type Endpoints = {
|
|||||||
};
|
};
|
||||||
"i/gallery/likes": { req: TODO; res: TODO };
|
"i/gallery/likes": { req: TODO; res: TODO };
|
||||||
"i/gallery/posts": { req: TODO; res: TODO };
|
"i/gallery/posts": { req: TODO; res: TODO };
|
||||||
"i/get-word-muted-notes-count": { req: TODO; res: TODO };
|
|
||||||
"i/import-following": { req: TODO; res: TODO };
|
"i/import-following": { req: TODO; res: TODO };
|
||||||
"i/import-user-lists": { req: TODO; res: TODO };
|
"i/import-user-lists": { req: TODO; res: TODO };
|
||||||
"i/move": { req: TODO; res: TODO };
|
"i/move": { req: TODO; res: TODO };
|
||||||
|
@ -20,8 +20,6 @@ export const noteVisibilities = [
|
|||||||
"specified",
|
"specified",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const mutedNoteReasons = ["word", "manual", "spam", "other"] as const;
|
|
||||||
|
|
||||||
export const ffVisibility = ["public", "followers", "private"] as const;
|
export const ffVisibility = ["public", "followers", "private"] as const;
|
||||||
|
|
||||||
export const permissions = [
|
export const permissions = [
|
||||||
|
@ -9,7 +9,6 @@ export { Endpoints, Stream, Connection as ChannelConnection, Channels, Acct };
|
|||||||
export const permissions = consts.permissions;
|
export const permissions = consts.permissions;
|
||||||
export const notificationTypes = consts.notificationTypes;
|
export const notificationTypes = consts.notificationTypes;
|
||||||
export const noteVisibilities = consts.noteVisibilities;
|
export const noteVisibilities = consts.noteVisibilities;
|
||||||
export const mutedNoteReasons = consts.mutedNoteReasons;
|
|
||||||
export const ffVisibility = consts.ffVisibility;
|
export const ffVisibility = consts.ffVisibility;
|
||||||
|
|
||||||
// api extractor not supported yet
|
// api extractor not supported yet
|
||||||
|
Loading…
Reference in New Issue
Block a user