diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts
index eae7645321..c6f4696c08 100644
--- a/packages/backend/src/server/web/FeedService.ts
+++ b/packages/backend/src/server/web/FeedService.ts
@@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { In, IsNull } from 'typeorm';
import { Feed } from 'feed';
import { DI } from '@/di-symbols.js';
-import type { DriveFilesRepository, NotesRepository, UserProfilesRepository } from '@/models/_.js';
+import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import type { Config } from '@/config.js';
import type { MiUser } from '@/models/User.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
@@ -16,13 +16,23 @@ import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { MfmService } from "@/core/MfmService.js";
import { parse as mfmParse } from 'mfm-js';
+import { MiNote } from '@/models/Note.js';
+import { isQuote, isRenote } from '@/misc/is-renote.js';
+import { getNoteSummary } from '@/misc/get-note-summary.js';
+import Logger from '@/logger.js';
+import { LoggerService } from '@/core/LoggerService.js';
@Injectable()
export class FeedService {
+ private readonly logger: Logger;
+
constructor(
@Inject(DI.config)
private config: Config,
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
+
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
@@ -36,13 +46,17 @@ export class FeedService {
private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
private mfmService: MfmService,
+
+ loggerService: LoggerService,
) {
+ this.logger = loggerService.getLogger('feed');
}
@bindThis
public async packFeed(user: MiUser) {
const author = {
link: `${this.config.url}/@${user.username}`,
+ email: `${user.username}@${this.config.host}`,
name: user.name ?? user.username,
};
@@ -51,7 +65,6 @@ export class FeedService {
const notes = await this.notesRepository.find({
where: {
userId: user.id,
- renoteId: IsNull(),
visibility: In(['public', 'home']),
},
order: { id: -1 },
@@ -75,22 +88,111 @@ export class FeedService {
});
for (const note of notes) {
- const files = note.fileIds.length > 0 ? await this.driveFilesRepository.findBy({
- id: In(note.fileIds),
- }) : [];
- const file = files.find(file => file.type.startsWith('image/'));
- const text = note.text;
+ let contentStr = await this.noteToString(note, true);
+ let next = note.renoteId ? note.renoteId : note.replyId;
+ let depth = 10;
+ const noteintitle = true;
+ let title = `Post by ${author.name}`;
+ while (depth > 0 && next) {
+ const finding = await this.findById(next);
+ contentStr += finding.text;
+ next = finding.next;
+ depth -= 1;
+ }
+
+ if (noteintitle) {
+ if (note.renoteId) {
+ title = `Boost by ${author.name}`;
+ } else if (note.replyId) {
+ title = `Reply by ${author.name}`;
+ } else {
+ title = `Post by ${author.name}`;
+ }
+ const effectiveNote =
+ !isQuote(note) && note.renote != null ? note.renote : note;
+ const content = getNoteSummary(effectiveNote);
+ if (content) {
+ title += `: ${content}`;
+ }
+ }
feed.addItem({
- title: `New note by ${author.name}`,
+ title: this.escapeCDATA(title).substring(0, 100),
link: `${this.config.url}/notes/${note.id}`,
date: this.idService.parse(note.id).date,
- description: note.cw ?? undefined,
- content: text ? this.mfmService.toHtml(mfmParse(text), JSON.parse(note.mentionedRemoteUsers)) ?? undefined : undefined,
- image: file ? this.driveFileEntityService.getPublicUrl(file) : undefined,
+ description: this.escapeCDATA(note.cw) ?? undefined,
+ content: this.escapeCDATA(contentStr) || undefined,
});
}
return feed;
}
+
+ private escapeCDATA(str: string) {
+ return str?.replaceAll("]]>", "]]]]>").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
+ }
+
+ private async noteToString(note: MiNote, isTheNote = false) {
+ const author = isTheNote
+ ? null
+ : await this.usersRepository.findOneByOrFail({ id: note.userId });
+ let outstr = author
+ ? `${author.name}(@${author.username}@${
+ author.host ? author.host : this.config.host
+ }) ${
+ note.renoteId ? "renotes" : note.replyId ? "replies" : "says"
+ }:
`
+ : "";
+ const files = note.fileIds?.length ? await this.driveFilesRepository.findBy({
+ id: In(note.fileIds),
+ }) : [];
+ let fileEle = "";
+ for (const file of files) {
+ if (file.type.startsWith("image/")) {
+ fileEle += `
`;
+ } else if (file.type.startsWith("audio/")) {
+ fileEle += `