なんかもうめっちゃ変えた

This commit is contained in:
syuilo
2022-09-18 03:27:08 +09:00
committed by GitHub
parent d9ab03f086
commit b75184ec8e
946 changed files with 41219 additions and 28839 deletions

View File

@@ -0,0 +1,62 @@
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import { HybridTimelineChannelService } from './channels/hybrid-timeline.js';
import { LocalTimelineChannelService } from './channels/local-timeline.js';
import { HomeTimelineChannelService } from './channels/home-timeline.js';
import { GlobalTimelineChannelService } from './channels/global-timeline.js';
import { MainChannelService } from './channels/main.js';
import { ChannelChannelService } from './channels/channel.js';
import { AdminChannelService } from './channels/admin.js';
import { ServerStatsChannelService } from './channels/server-stats.js';
import { QueueStatsChannelService } from './channels/queue-stats.js';
import { UserListChannelService } from './channels/user-list.js';
import { AntennaChannelService } from './channels/antenna.js';
import { MessagingChannelService } from './channels/messaging.js';
import { MessagingIndexChannelService } from './channels/messaging-index.js';
import { DriveChannelService } from './channels/drive.js';
import { HashtagChannelService } from './channels/hashtag.js';
@Injectable()
export class ChannelsService {
constructor(
private mainChannelService: MainChannelService,
private homeTimelineChannelService: HomeTimelineChannelService,
private localTimelineChannelService: LocalTimelineChannelService,
private hybridTimelineChannelService: HybridTimelineChannelService,
private globalTimelineChannelService: GlobalTimelineChannelService,
private userListChannelService: UserListChannelService,
private hashtagChannelService: HashtagChannelService,
private antennaChannelService: AntennaChannelService,
private channelChannelService: ChannelChannelService,
private messagingChannelService: MessagingChannelService,
private messagingIndexChannelService: MessagingIndexChannelService,
private driveChannelService: DriveChannelService,
private serverStatsChannelService: ServerStatsChannelService,
private queueStatsChannelService: QueueStatsChannelService,
private adminChannelService: AdminChannelService,
) {
}
public getChannelService(name: string) {
switch (name) {
case 'main': return this.mainChannelService;
case 'homeTimeline': return this.homeTimelineChannelService;
case 'localTimeline': return this.localTimelineChannelService;
case 'hybridTimeline': return this.hybridTimelineChannelService;
case 'globalTimeline': return this.globalTimelineChannelService;
case 'userList': return this.userListChannelService;
case 'hashtag': return this.hashtagChannelService;
case 'antenna': return this.antennaChannelService;
case 'channel': return this.channelChannelService;
case 'messaging': return this.messagingChannelService;
case 'messagingIndex': return this.messagingIndexChannelService;
case 'drive': return this.driveChannelService;
case 'serverStats': return this.serverStatsChannelService;
case 'queueStats': return this.queueStatsChannelService;
case 'admin': return this.adminChannelService;
default:
throw new Error(`no such channel: ${name}`);
}
}
}

View File

@@ -1,4 +1,4 @@
import Connection from '.';
import type Connection from '.';
/**
* Stream channel

View File

@@ -1,6 +1,7 @@
import { Inject, Injectable } from '@nestjs/common';
import Channel from '../channel.js';
export default class extends Channel {
class AdminChannel extends Channel {
public readonly chName = 'admin';
public static shouldShare = true;
public static requireCredential = true;
@@ -12,3 +13,20 @@ export default class extends Channel {
});
}
}
@Injectable()
export class AdminChannelService {
public readonly shouldShare = AdminChannel.shouldShare;
public readonly requireCredential = AdminChannel.requireCredential;
constructor(
) {
}
public create(id: string, connection: Channel['connection']): AdminChannel {
return new AdminChannel(
id,
connection,
);
}
}

View File

@@ -1,15 +1,22 @@
import Channel from '../channel.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { StreamMessages } from '../types.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
import type { StreamMessages } from '../types.js';
export default class extends Channel {
class AntennaChannel extends Channel {
public readonly chName = 'antenna';
public static shouldShare = false;
public static requireCredential = false;
private antennaId: string;
constructor(id: string, connection: Channel['connection']) {
constructor(
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onEvent = this.onEvent.bind(this);
}
@@ -23,7 +30,7 @@ export default class extends Channel {
private async onEvent(data: StreamMessages['antenna']['payload']) {
if (data.type === 'note') {
const note = await Notes.pack(data.body.id, this.user, { detail: true });
const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.muting)) return;
@@ -43,3 +50,22 @@ export default class extends Channel {
this.subscriber.off(`antennaStream:${this.antennaId}`, this.onEvent);
}
}
@Injectable()
export class AntennaChannelService {
public readonly shouldShare = AntennaChannel.shouldShare;
public readonly requireCredential = AntennaChannel.requireCredential;
constructor(
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): AntennaChannel {
return new AntennaChannel(
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,11 +1,14 @@
import Channel from '../channel.js';
import { Notes, Users } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository, UsersRepository } from '@/models/index.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { User } from '@/models/entities/user.js';
import { StreamMessages } from '../types.js';
import { Packed } from '@/misc/schema.js';
import type { User } from '@/models/entities/User.js';
import type { Packed } from '@/misc/schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import Channel from '../channel.js';
import type { StreamMessages } from '../types.js';
export default class extends Channel {
class ChannelChannel extends Channel {
public readonly chName = 'channel';
public static shouldShare = false;
public static requireCredential = false;
@@ -13,7 +16,13 @@ export default class extends Channel {
private typers: Record<User['id'], Date> = {};
private emitTypersIntervalId: ReturnType<typeof setInterval>;
constructor(id: string, connection: Channel['connection']) {
constructor(
private noteEntityService: NoteEntityService,
private userEntityService: UserEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onNote = this.onNote.bind(this);
this.emitTypers = this.emitTypers.bind(this);
@@ -33,13 +42,13 @@ export default class extends Channel {
// リプライなら再pack
if (note.replyId != null) {
note.reply = await Notes.pack(note.replyId, this.user, {
note.reply = await this.noteEntityService.pack(note.replyId, this.user, {
detail: true,
});
}
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user, {
detail: true,
});
}
@@ -73,7 +82,7 @@ export default class extends Channel {
if (now.getTime() - date.getTime() > 5000) delete this.typers[userId];
}
const users = await Users.packMany(Object.keys(this.typers), null, { detail: false });
const users = await this.userEntityService.packMany(Object.keys(this.typers), null, { detail: false });
this.send({
type: 'typers',
@@ -89,3 +98,24 @@ export default class extends Channel {
clearInterval(this.emitTypersIntervalId);
}
}
@Injectable()
export class ChannelChannelService {
public readonly shouldShare = ChannelChannel.shouldShare;
public readonly requireCredential = ChannelChannel.requireCredential;
constructor(
private noteEntityService: NoteEntityService,
private userEntityService: UserEntityService,
) {
}
public create(id: string, connection: Channel['connection']): ChannelChannel {
return new ChannelChannel(
this.noteEntityService,
this.userEntityService,
id,
connection,
);
}
}

View File

@@ -1,6 +1,7 @@
import { Inject, Injectable } from '@nestjs/common';
import Channel from '../channel.js';
export default class extends Channel {
class DriveChannel extends Channel {
public readonly chName = 'drive';
public static shouldShare = true;
public static requireCredential = true;
@@ -12,3 +13,20 @@ export default class extends Channel {
});
}
}
@Injectable()
export class DriveChannelService {
public readonly shouldShare = DriveChannel.shouldShare;
public readonly requireCredential = DriveChannel.requireCredential;
constructor(
) {
}
public create(id: string, connection: Channel['connection']): DriveChannel {
return new DriveChannel(
id,
connection,
);
}
}

View File

@@ -1,23 +1,31 @@
import Channel from '../channel.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { Packed } from '@/misc/schema.js';
import type { Packed } from '@/misc/schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
export default class extends Channel {
class GlobalTimelineChannel extends Channel {
public readonly chName = 'globalTimeline';
public static shouldShare = true;
public static requireCredential = false;
constructor(id: string, connection: Channel['connection']) {
constructor(
private metaService: MetaService,
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onNote = this.onNote.bind(this);
}
public async init(params: any) {
const meta = await fetchMeta();
const meta = await this.metaService.fetch();
if (meta.disableGlobalTimeline) {
if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return;
}
@@ -32,13 +40,13 @@ export default class extends Channel {
// リプライなら再pack
if (note.replyId != null) {
note.reply = await Notes.pack(note.replyId, this.user, {
note.reply = await this.noteEntityService.pack(note.replyId, this.user, {
detail: true,
});
}
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user, {
detail: true,
});
}
@@ -75,3 +83,24 @@ export default class extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
@Injectable()
export class GlobalTimelineChannelService {
public readonly shouldShare = GlobalTimelineChannel.shouldShare;
public readonly requireCredential = GlobalTimelineChannel.requireCredential;
constructor(
private metaService: MetaService,
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): GlobalTimelineChannel {
return new GlobalTimelineChannel(
this.metaService,
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,16 +1,23 @@
import Channel from '../channel.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { Packed } from '@/misc/schema.js';
import type { Packed } from '@/misc/schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
export default class extends Channel {
class HashtagChannel extends Channel {
public readonly chName = 'hashtag';
public static shouldShare = false;
public static requireCredential = false;
private q: string[][];
constructor(id: string, connection: Channel['connection']) {
constructor(
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onNote = this.onNote.bind(this);
}
@@ -31,7 +38,7 @@ export default class extends Channel {
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user, {
detail: true,
});
}
@@ -51,3 +58,22 @@ export default class extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
@Injectable()
export class HashtagChannelService {
public readonly shouldShare = HashtagChannel.shouldShare;
public readonly requireCredential = HashtagChannel.requireCredential;
constructor(
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): HashtagChannel {
return new HashtagChannel(
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,16 +1,23 @@
import Channel from '../channel.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { Packed } from '@/misc/schema.js';
import type { Packed } from '@/misc/schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
export default class extends Channel {
class HomeTimelineChannel extends Channel {
public readonly chName = 'homeTimeline';
public static shouldShare = true;
public static requireCredential = true;
constructor(id: string, connection: Channel['connection']) {
constructor(
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onNote = this.onNote.bind(this);
}
@@ -32,7 +39,7 @@ export default class extends Channel {
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
if (['followers', 'specified'].includes(note.visibility)) {
note = await Notes.pack(note.id, this.user!, {
note = await this.noteEntityService.pack(note.id, this.user!, {
detail: true,
});
@@ -42,13 +49,13 @@ export default class extends Channel {
} else {
// リプライなら再pack
if (note.replyId != null) {
note.reply = await Notes.pack(note.replyId, this.user!, {
note.reply = await this.noteEntityService.pack(note.replyId, this.user!, {
detail: true,
});
}
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user!, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, {
detail: true,
});
}
@@ -83,3 +90,22 @@ export default class extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
@Injectable()
export class HomeTimelineChannelService {
public readonly shouldShare = HomeTimelineChannel.shouldShare;
public readonly requireCredential = HomeTimelineChannel.requireCredential;
constructor(
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): HomeTimelineChannel {
return new HomeTimelineChannel(
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,23 +1,32 @@
import Channel from '../channel.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { Packed } from '@/misc/schema.js';
import type { Packed } from '@/misc/schema.js';
import { DI } from '@/di-symbols.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
export default class extends Channel {
class HybridTimelineChannel extends Channel {
public readonly chName = 'hybridTimeline';
public static shouldShare = true;
public static requireCredential = true;
constructor(id: string, connection: Channel['connection']) {
constructor(
private metaService: MetaService,
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onNote = this.onNote.bind(this);
}
public async init(params: any) {
const meta = await fetchMeta();
public async init(params: any): Promise<void> {
const meta = await this.metaService.fetch();
if (meta.disableLocalTimeline && !this.user!.isAdmin && !this.user!.isModerator) return;
// Subscribe events
@@ -37,7 +46,7 @@ export default class extends Channel {
)) return;
if (['followers', 'specified'].includes(note.visibility)) {
note = await Notes.pack(note.id, this.user!, {
note = await this.noteEntityService.pack(note.id, this.user!, {
detail: true,
});
@@ -47,13 +56,13 @@ export default class extends Channel {
} else {
// リプライなら再pack
if (note.replyId != null) {
note.reply = await Notes.pack(note.replyId, this.user!, {
note.reply = await this.noteEntityService.pack(note.replyId, this.user!, {
detail: true,
});
}
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user!, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, {
detail: true,
});
}
@@ -86,8 +95,29 @@ export default class extends Channel {
this.send('note', note);
}
public dispose() {
public dispose(): void {
// Unsubscribe events
this.subscriber.off('notesStream', this.onNote);
}
}
@Injectable()
export class HybridTimelineChannelService {
public readonly shouldShare = HybridTimelineChannel.shouldShare;
public readonly requireCredential = HybridTimelineChannel.requireCredential;
constructor(
private metaService: MetaService,
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): HybridTimelineChannel {
return new HybridTimelineChannel(
this.metaService,
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,33 +0,0 @@
import main from './main.js';
import homeTimeline from './home-timeline.js';
import localTimeline from './local-timeline.js';
import hybridTimeline from './hybrid-timeline.js';
import globalTimeline from './global-timeline.js';
import serverStats from './server-stats.js';
import queueStats from './queue-stats.js';
import userList from './user-list.js';
import antenna from './antenna.js';
import messaging from './messaging.js';
import messagingIndex from './messaging-index.js';
import drive from './drive.js';
import hashtag from './hashtag.js';
import channel from './channel.js';
import admin from './admin.js';
export default {
main,
homeTimeline,
localTimeline,
hybridTimeline,
globalTimeline,
serverStats,
queueStats,
userList,
antenna,
messaging,
messagingIndex,
drive,
hashtag,
channel,
admin,
};

View File

@@ -1,22 +1,30 @@
import Channel from '../channel.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { Packed } from '@/misc/schema.js';
import type { Packed } from '@/misc/schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
export default class extends Channel {
class LocalTimelineChannel extends Channel {
public readonly chName = 'localTimeline';
public static shouldShare = true;
public static requireCredential = false;
constructor(id: string, connection: Channel['connection']) {
constructor(
private metaService: MetaService,
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onNote = this.onNote.bind(this);
}
public async init(params: any) {
const meta = await fetchMeta();
const meta = await this.metaService.fetch();
if (meta.disableLocalTimeline) {
if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return;
}
@@ -32,13 +40,13 @@ export default class extends Channel {
// リプライなら再pack
if (note.replyId != null) {
note.reply = await Notes.pack(note.replyId, this.user, {
note.reply = await this.noteEntityService.pack(note.replyId, this.user, {
detail: true,
});
}
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user, {
detail: true,
});
}
@@ -72,3 +80,24 @@ export default class extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
@Injectable()
export class LocalTimelineChannelService {
public readonly shouldShare = LocalTimelineChannel.shouldShare;
public readonly requireCredential = LocalTimelineChannel.requireCredential;
constructor(
private metaService: MetaService,
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): LocalTimelineChannel {
return new LocalTimelineChannel(
this.metaService,
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,12 +1,23 @@
import Channel from '../channel.js';
import { Notes } from '@/models/index.js';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import Channel from '../channel.js';
export default class extends Channel {
class MainChannel extends Channel {
public readonly chName = 'main';
public static shouldShare = true;
public static requireCredential = true;
constructor(
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
}
public async init(params: any) {
// Subscribe main stream channel
this.subscriber.on(`mainStream:${this.user!.id}`, async data => {
@@ -17,7 +28,7 @@ export default class extends Channel {
if (data.body.userId && this.muting.has(data.body.userId)) return;
if (data.body.note && data.body.note.isHidden) {
const note = await Notes.pack(data.body.note.id, this.user, {
const note = await this.noteEntityService.pack(data.body.note.id, this.user, {
detail: true,
});
this.connection.cacheNote(note);
@@ -30,7 +41,7 @@ export default class extends Channel {
if (this.muting.has(data.body.userId)) return;
if (data.body.isHidden) {
const note = await Notes.pack(data.body.id, this.user, {
const note = await this.noteEntityService.pack(data.body.id, this.user, {
detail: true,
});
this.connection.cacheNote(note);
@@ -44,3 +55,22 @@ export default class extends Channel {
});
}
}
@Injectable()
export class MainChannelService {
public readonly shouldShare = MainChannel.shouldShare;
public readonly requireCredential = MainChannel.requireCredential;
constructor(
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): MainChannel {
return new MainChannel(
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,6 +1,7 @@
import { Inject, Injectable } from '@nestjs/common';
import Channel from '../channel.js';
export default class extends Channel {
class MessagingIndexChannel extends Channel {
public readonly chName = 'messagingIndex';
public static shouldShare = true;
public static requireCredential = true;
@@ -12,3 +13,20 @@ export default class extends Channel {
});
}
}
@Injectable()
export class MessagingIndexChannelService {
public readonly shouldShare = MessagingIndexChannel.shouldShare;
public readonly requireCredential = MessagingIndexChannel.requireCredential;
constructor(
) {
}
public create(id: string, connection: Channel['connection']): MessagingIndexChannel {
return new MessagingIndexChannel(
id,
connection,
);
}
}

View File

@@ -1,11 +1,14 @@
import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js';
import { Inject, Injectable } from '@nestjs/common';
import { UserGroupJoiningsRepository, UsersRepository, MessagingMessagesRepository } from '@/models/index.js';
import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js';
import type { UserGroup } from '@/models/entities/UserGroup.js';
import { MessagingService } from '@/core/MessagingService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
import Channel from '../channel.js';
import { UserGroupJoinings, Users, MessagingMessages } from '@/models/index.js';
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
import { UserGroup } from '@/models/entities/user-group.js';
import { StreamMessages } from '../types.js';
import type { StreamMessages } from '../types.js';
export default class extends Channel {
class MessagingChannel extends Channel {
public readonly chName = 'messaging';
public static shouldShare = false;
public static requireCredential = true;
@@ -17,7 +20,16 @@ export default class extends Channel {
private typers: Record<User['id'], Date> = {};
private emitTypersIntervalId: ReturnType<typeof setInterval>;
constructor(id: string, connection: Channel['connection']) {
constructor(
private usersRepository: UsersRepository,
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
private messagingMessagesRepository: MessagingMessagesRepository,
private userEntityService: UserEntityService,
private messagingService: MessagingService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.onEvent = this.onEvent.bind(this);
this.onMessage = this.onMessage.bind(this);
@@ -26,12 +38,12 @@ export default class extends Channel {
public async init(params: any) {
this.otherpartyId = params.otherparty;
this.otherparty = this.otherpartyId ? await Users.findOneByOrFail({ id: this.otherpartyId }) : null;
this.otherparty = this.otherpartyId ? await this.usersRepository.findOneByOrFail({ id: this.otherpartyId }) : null;
this.groupId = params.group;
// Check joining
if (this.groupId) {
const joining = await UserGroupJoinings.findOneBy({
const joining = await this.userGroupJoiningsRepository.findOneBy({
userId: this.user!.id,
userGroupId: this.groupId,
});
@@ -68,16 +80,16 @@ export default class extends Channel {
switch (type) {
case 'read':
if (this.otherpartyId) {
readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]);
this.messagingService.readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]);
// リモートユーザーからのメッセージだったら既読配信
if (Users.isLocalUser(this.user!) && Users.isRemoteUser(this.otherparty!)) {
MessagingMessages.findOneBy({ id: body.id }).then(message => {
if (message) deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message);
if (this.userEntityService.isLocalUser(this.user!) && this.userEntityService.isRemoteUser(this.otherparty!)) {
this.messagingMessagesRepository.findOneBy({ id: body.id }).then(message => {
if (message) this.messagingService.deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message);
});
}
} else if (this.groupId) {
readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]);
this.messagingService.readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]);
}
break;
}
@@ -91,7 +103,7 @@ export default class extends Channel {
if (now.getTime() - date.getTime() > 5000) delete this.typers[userId];
}
const users = await Users.packMany(Object.keys(this.typers), null, { detail: false });
const users = await this.userEntityService.packMany(Object.keys(this.typers), null, { detail: false });
this.send({
type: 'typers',
@@ -105,3 +117,36 @@ export default class extends Channel {
clearInterval(this.emitTypersIntervalId);
}
}
@Injectable()
export class MessagingChannelService {
public readonly shouldShare = MessagingChannel.shouldShare;
public readonly requireCredential = MessagingChannel.requireCredential;
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.userGroupJoiningsRepository)
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
@Inject(DI.messagingMessagesRepository)
private messagingMessagesRepository: MessagingMessagesRepository,
private userEntityService: UserEntityService,
private messagingService: MessagingService,
) {
}
public create(id: string, connection: Channel['connection']): MessagingChannel {
return new MessagingChannel(
this.usersRepository,
this.userGroupJoiningsRepository,
this.messagingMessagesRepository,
this.userEntityService,
this.messagingService,
id,
connection,
);
}
}

View File

@@ -1,9 +1,10 @@
import Xev from 'xev';
import { Inject, Injectable } from '@nestjs/common';
import Channel from '../channel.js';
const ev = new Xev();
export default class extends Channel {
class QueueStatsChannel extends Channel {
public readonly chName = 'queueStats';
public static shouldShare = true;
public static requireCredential = false;
@@ -40,3 +41,20 @@ export default class extends Channel {
ev.removeListener('queueStats', this.onStats);
}
}
@Injectable()
export class QueueStatsChannelService {
public readonly shouldShare = QueueStatsChannel.shouldShare;
public readonly requireCredential = QueueStatsChannel.requireCredential;
constructor(
) {
}
public create(id: string, connection: Channel['connection']): QueueStatsChannel {
return new QueueStatsChannel(
id,
connection,
);
}
}

View File

@@ -1,9 +1,10 @@
import Xev from 'xev';
import { Inject, Injectable } from '@nestjs/common';
import Channel from '../channel.js';
const ev = new Xev();
export default class extends Channel {
class ServerStatsChannel extends Channel {
public readonly chName = 'serverStats';
public static shouldShare = true;
public static requireCredential = false;
@@ -40,3 +41,20 @@ export default class extends Channel {
ev.removeListener('serverStats', this.onStats);
}
}
@Injectable()
export class ServerStatsChannelService {
public readonly shouldShare = ServerStatsChannel.shouldShare;
public readonly requireCredential = ServerStatsChannel.requireCredential;
constructor(
) {
}
public create(id: string, connection: Channel['connection']): ServerStatsChannel {
return new ServerStatsChannel(
id,
connection,
);
}
}

View File

@@ -1,10 +1,14 @@
import Channel from '../channel.js';
import { Notes, UserListJoinings, UserLists } from '@/models/index.js';
import { User } from '@/models/entities/user.js';
import { Inject, Injectable } from '@nestjs/common';
import { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js';
import type { NotesRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { Packed } from '@/misc/schema.js';
import type { Packed } from '@/misc/schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import Channel from '../channel.js';
export default class extends Channel {
class UserListChannel extends Channel {
public readonly chName = 'userList';
public static shouldShare = false;
public static requireCredential = false;
@@ -12,7 +16,14 @@ export default class extends Channel {
public listUsers: User['id'][] = [];
private listUsersClock: NodeJS.Timer;
constructor(id: string, connection: Channel['connection']) {
constructor(
private userListsRepository: UserListsRepository,
private userListJoiningsRepository: UserListJoiningsRepository,
private noteEntityService: NoteEntityService,
id: string,
connection: Channel['connection'],
) {
super(id, connection);
this.updateListUsers = this.updateListUsers.bind(this);
this.onNote = this.onNote.bind(this);
@@ -22,7 +33,7 @@ export default class extends Channel {
this.listId = params.listId as string;
// Check existence and owner
const list = await UserLists.findOneBy({
const list = await this.userListsRepository.findOneBy({
id: this.listId,
userId: this.user!.id,
});
@@ -38,7 +49,7 @@ export default class extends Channel {
}
private async updateListUsers() {
const users = await UserListJoinings.find({
const users = await this.userListJoiningsRepository.find({
where: {
userListId: this.listId,
},
@@ -52,7 +63,7 @@ export default class extends Channel {
if (!this.listUsers.includes(note.userId)) return;
if (['followers', 'specified'].includes(note.visibility)) {
note = await Notes.pack(note.id, this.user, {
note = await this.noteEntityService.pack(note.id, this.user, {
detail: true,
});
@@ -62,13 +73,13 @@ export default class extends Channel {
} else {
// リプライなら再pack
if (note.replyId != null) {
note.reply = await Notes.pack(note.replyId, this.user, {
note.reply = await this.noteEntityService.pack(note.replyId, this.user, {
detail: true,
});
}
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await Notes.pack(note.renoteId, this.user, {
note.renote = await this.noteEntityService.pack(note.renoteId, this.user, {
detail: true,
});
}
@@ -90,3 +101,30 @@ export default class extends Channel {
clearInterval(this.listUsersClock);
}
}
@Injectable()
export class UserListChannelService {
public readonly shouldShare = UserListChannel.shouldShare;
public readonly requireCredential = UserListChannel.requireCredential;
constructor(
@Inject(DI.userListsRepository)
private userListsRepository: UserListsRepository,
@Inject(DI.userListJoiningsRepository)
private userListJoiningsRepository: UserListJoiningsRepository,
private noteEntityService: NoteEntityService,
) {
}
public create(id: string, connection: Channel['connection']): UserListChannel {
return new UserListChannel(
this.userListsRepository,
this.userListJoiningsRepository,
this.noteEntityService,
id,
connection,
);
}
}

View File

@@ -1,18 +1,18 @@
import { EventEmitter } from 'events';
import * as websocket from 'websocket';
import readNote from '@/services/note/read.js';
import { User } from '@/models/entities/user.js';
import { Channel as ChannelModel } from '@/models/entities/channel.js';
import { Users, Followings, Mutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js';
import { AccessToken } from '@/models/entities/access-token.js';
import { UserProfile } from '@/models/entities/user-profile.js';
import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream.js';
import { UserGroup } from '@/models/entities/user-group.js';
import { Packed } from '@/misc/schema.js';
import { readNotification } from '../common/read-notification.js';
import channels from './channels/index.js';
import Channel from './channel.js';
import { StreamEventEmitter, StreamMessages } from './types.js';
import type { User } from '@/models/entities/User.js';
import type { Channel as ChannelModel } from '@/models/entities/Channel.js';
import type { FollowingsRepository, MutingsRepository, UserProfilesRepository, ChannelFollowingsRepository, BlockingsRepository } from '@/models/index.js';
import type { AccessToken } from '@/models/entities/AccessToken.js';
import type { UserProfile } from '@/models/entities/UserProfile.js';
import type { UserGroup } from '@/models/entities/UserGroup.js';
import type { Packed } from '@/misc/schema.js';
import type { GlobalEventService } from '@/core/GlobalEventService.js';
import type { NoteReadService } from '@/core/NoteReadService.js';
import type { NotificationService } from '@/core/NotificationService.js';
import type { ChannelsService } from './ChannelsService.js';
import type * as websocket from 'websocket';
import type { EventEmitter } from 'events';
import type Channel from './channel.js';
import type { StreamEventEmitter, StreamMessages } from './types.js';
/**
* Main stream connection
@@ -32,6 +32,16 @@ export default class Connection {
private cachedNotes: Packed<'Note'>[] = [];
constructor(
private followingsRepository: FollowingsRepository,
private mutingsRepository: MutingsRepository,
private blockingsRepository: BlockingsRepository,
private channelFollowingsRepository: ChannelFollowingsRepository,
private userProfilesRepository: UserProfilesRepository,
private channelsService: ChannelsService,
private globalEventService: GlobalEventService,
private noteReadService: NoteReadService,
private notificationService: NotificationService,
wsConnection: websocket.connection,
subscriber: EventEmitter,
user: User | null | undefined,
@@ -173,7 +183,7 @@ export default class Connection {
if (note == null) return;
if (this.user && (note.userId !== this.user.id)) {
readNote(this.user.id, [note], {
this.noteReadService.read(this.user.id, [note], {
following: this.following,
followingChannels: this.followingChannels,
});
@@ -182,7 +192,7 @@ export default class Connection {
private onReadNotification(payload: any) {
if (!payload.id) return;
readNotification(this.user!.id, [payload.id]);
this.notificationService.readNotification(this.user!.id, [payload.id]);
}
/**
@@ -253,16 +263,18 @@ export default class Connection {
* チャンネルに接続
*/
public connectChannel(id: string, params: any, channel: string, pong = false) {
if ((channels as any)[channel].requireCredential && this.user == null) {
const channelService = this.channelsService.getChannelService(channel);
if (channelService.requireCredential && this.user == null) {
return;
}
// 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視
if ((channels as any)[channel].shouldShare && this.channels.some(c => c.chName === channel)) {
if (channelService.shouldShare && this.channels.some(c => c.chName === channel)) {
return;
}
const ch: Channel = new (channels as any)[channel](id, this);
const ch: Channel = channelService.create(id, this);
this.channels.push(ch);
ch.init(params);
@@ -299,22 +311,22 @@ export default class Connection {
private typingOnChannel(channel: ChannelModel['id']) {
if (this.user) {
publishChannelStream(channel, 'typing', this.user.id);
this.globalEventService.publishChannelStream(channel, 'typing', this.user.id);
}
}
private typingOnMessaging(param: { partner?: User['id']; group?: UserGroup['id']; }) {
if (this.user) {
if (param.partner) {
publishMessagingStream(param.partner, this.user.id, 'typing', this.user.id);
this.globalEventService.publishMessagingStream(param.partner, this.user.id, 'typing', this.user.id);
} else if (param.group) {
publishGroupMessagingStream(param.group, 'typing', this.user.id);
this.globalEventService.publishGroupMessagingStream(param.group, 'typing', this.user.id);
}
}
}
private async updateFollowing() {
const followings = await Followings.find({
const followings = await this.followingsRepository.find({
where: {
followerId: this.user!.id,
},
@@ -325,7 +337,7 @@ export default class Connection {
}
private async updateMuting() {
const mutings = await Mutings.find({
const mutings = await this.mutingsRepository.find({
where: {
muterId: this.user!.id,
},
@@ -336,7 +348,7 @@ export default class Connection {
}
private async updateBlocking() { // ここでいうBlockingは被Blockingの意
const blockings = await Blockings.find({
const blockings = await this.blockingsRepository.find({
where: {
blockeeId: this.user!.id,
},
@@ -347,7 +359,7 @@ export default class Connection {
}
private async updateFollowingChannels() {
const followings = await ChannelFollowings.find({
const followings = await this.channelFollowingsRepository.find({
where: {
followerId: this.user!.id,
},
@@ -358,7 +370,7 @@ export default class Connection {
}
private async updateUserProfile() {
this.userProfile = await UserProfiles.findOneBy({
this.userProfile = await this.userProfilesRepository.findOneBy({
userId: this.user!.id,
});
}

View File

@@ -1,21 +1,20 @@
import { EventEmitter } from 'events';
import Emitter from 'strict-event-emitter-types';
import { Channel } from '@/models/entities/channel.js';
import { User } from '@/models/entities/user.js';
import { UserProfile } from '@/models/entities/user-profile.js';
import { Note } from '@/models/entities/note.js';
import { Antenna } from '@/models/entities/antenna.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { DriveFolder } from '@/models/entities/drive-folder.js';
import { Emoji } from '@/models/entities/emoji.js';
import { UserList } from '@/models/entities/user-list.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { UserGroup } from '@/models/entities/user-group.js';
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
import { Signin } from '@/models/entities/signin.js';
import { Page } from '@/models/entities/page.js';
import { Packed } from '@/misc/schema.js';
import { Webhook } from '@/models/entities/webhook';
import type { Channel } from '@/models/entities/Channel.js';
import type { User } from '@/models/entities/User.js';
import type { UserProfile } from '@/models/entities/UserProfile.js';
import type { Note } from '@/models/entities/Note.js';
import type { Antenna } from '@/models/entities/Antenna.js';
import type { DriveFile } from '@/models/entities/DriveFile.js';
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
import type { UserList } from '@/models/entities/UserList.js';
import type { MessagingMessage } from '@/models/entities/MessagingMessage.js';
import type { UserGroup } from '@/models/entities/UserGroup.js';
import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
import type { Signin } from '@/models/entities/Signin.js';
import type { Page } from '@/models/entities/Page.js';
import type { Packed } from '@/misc/schema.js';
import type { Webhook } from '@/models/entities/Webhook.js';
import type Emitter from 'strict-event-emitter-types';
import type { EventEmitter } from 'events';
//#region Stream type-body definitions
export interface InternalStreamTypes {