This commit is contained in:
anatawa12 2025-05-09 17:46:44 +09:00 committed by GitHub
commit ef7034ba83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 39 additions and 11 deletions

View File

@ -15,7 +15,7 @@
- Enhance: メモリ使用量を軽減しました - Enhance: メモリ使用量を軽減しました
### Server ### Server
- - Enhance: リストやフォローをエクスポートする際にリプライを含むかどうかの情報を含むように
## 2025.5.0 ## 2025.5.0

2
locales/index.d.ts vendored
View File

@ -9599,7 +9599,7 @@ export interface Locale extends ILocale {
*/ */
"excludeInactiveUsers": string; "excludeInactiveUsers": string;
/** /**
* TLに含むようにする * TLに含むかの情報がファイルにない場合にTLに含むようにする
*/ */
"withReplies": string; "withReplies": string;
}; };

View File

@ -2529,7 +2529,7 @@ _exportOrImport:
userLists: "リスト" userLists: "リスト"
excludeMutingUsers: "ミュートしているユーザーを除外" excludeMutingUsers: "ミュートしているユーザーを除外"
excludeInactiveUsers: "使われていないアカウントを除外" excludeInactiveUsers: "使われていないアカウントを除外"
withReplies: "インポートした人による返信をTLに含むようにする" withReplies: "返信をTLに含むかの情報がファイルにない場合に、インポートした人による返信をTLに含むようにする"
_charts: _charts:
federation: "連合" federation: "連合"

View File

@ -91,7 +91,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
} }
@bindThis @bindThis
public async addMember(target: MiUser, list: MiUserList, me: MiUser) { public async addMember(target: MiUser, list: MiUserList, me: MiUser, options: { withReplies?: boolean } = {}) {
const currentCount = await this.userListMembershipsRepository.countBy({ const currentCount = await this.userListMembershipsRepository.countBy({
userListId: list.id, userListId: list.id,
}); });
@ -104,6 +104,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
userId: target.id, userId: target.id,
userListId: list.id, userListId: list.id,
userListUserId: list.userId, userListUserId: list.userId,
withReplies: options.withReplies ?? false,
} as MiUserListMembership); } as MiUserListMembership);
this.globalEventService.publishInternalEvent('userListMemberAdded', { userListId: list.id, memberId: target.id }); this.globalEventService.publishInternalEvent('userListMemberAdded', { userListId: list.id, memberId: target.id });

View File

@ -94,7 +94,8 @@ export class ExportFollowingProcessorService {
continue; continue;
} }
const content = this.utilityService.getFullApAccount(u.username, u.host); const userAcct = this.utilityService.getFullApAccount(u.username, u.host);
const content = `${userAcct},withReplies=${following.withReplies}`;
await new Promise<void>((res, rej) => { await new Promise<void>((res, rej) => {
stream.write(content + '\n', err => { stream.write(content + '\n', err => {
if (err) { if (err) {

View File

@ -67,10 +67,12 @@ export class ExportUserListsProcessorService {
const users = await this.usersRepository.findBy({ const users = await this.usersRepository.findBy({
id: In(memberships.map(j => j.userId)), id: In(memberships.map(j => j.userId)),
}); });
const usersWithReplies = new Set(memberships.filter(m => m.withReplies).map(m => m.userId));
for (const u of users) { for (const u of users) {
const acct = this.utilityService.getFullApAccount(u.username, u.host); const acct = this.utilityService.getFullApAccount(u.username, u.host);
const content = `${list.name},${acct}`; // 3rd column and later will be key=value pairs
const content = `${list.name},${acct},withReplies=${usersWithReplies.has(u.id)}`;
await new Promise<void>((res, rej) => { await new Promise<void>((res, rej) => {
stream.write(content + '\n', err => { stream.write(content + '\n', err => {
if (err) { if (err) {

View File

@ -67,8 +67,19 @@ export class ImportFollowingProcessorService {
const user = job.data.user; const user = job.data.user;
try { try {
const acct = line.split(',')[0].trim(); const parts = line.split(',');
const acct = parts[0].trim();
const { username, host } = Acct.parse(acct); const { username, host } = Acct.parse(acct);
let withReplies: boolean | null = null;
for (const keyValue of parts.slice(2)) {
const [key, value] = keyValue.split('=');
switch (key) {
case 'withReplies':
withReplies = value === 'true';
break;
}
}
if (!host) return; if (!host) return;
@ -95,7 +106,7 @@ export class ImportFollowingProcessorService {
this.logger.info(`Follow ${target.id} ${job.data.withReplies ? 'with replies' : 'without replies'} ...`); this.logger.info(`Follow ${target.id} ${job.data.withReplies ? 'with replies' : 'without replies'} ...`);
this.queueService.createFollowJob([{ from: user, to: { id: target.id }, silent: true, withReplies: job.data.withReplies }]); await this.queueService.createFollowJob([{ from: user, to: { id: target.id }, silent: true, withReplies: withReplies ?? job.data.withReplies }]);
} catch (e) { } catch (e) {
this.logger.warn(`Error: ${e}`); this.logger.warn(`Error: ${e}`);
} }

View File

@ -70,8 +70,19 @@ export class ImportUserListsProcessorService {
linenum++; linenum++;
try { try {
const listName = line.split(',')[0].trim(); const parts = line.split(',');
const { username, host } = Acct.parse(line.split(',')[1].trim()); const listName = parts[0].trim();
const { username, host } = Acct.parse(parts[1].trim());
let withReplies = false;
for (const keyValue of parts.slice(2)) {
const [key, value] = keyValue.split('=');
switch (key) {
case 'withReplies':
withReplies = value === 'true';
break;
}
}
let list = await this.userListsRepository.findOneBy({ let list = await this.userListsRepository.findOneBy({
userId: user.id, userId: user.id,
@ -100,7 +111,9 @@ export class ImportUserListsProcessorService {
if (await this.userListMembershipsRepository.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue; if (await this.userListMembershipsRepository.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue;
this.userListService.addMember(target, list!, user); await this.userListService.addMember(target, list, user, {
withReplies: withReplies,
});
} catch (e) { } catch (e) {
this.logger.warn(`Error in line:${linenum} ${e}`); this.logger.warn(`Error in line:${linenum} ${e}`);
} }