Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
export class MastodonStream {
|
||||
connection;
|
||||
chName;
|
||||
static shouldShare;
|
||||
static requireCredential;
|
||||
static requiredScopes = [];
|
||||
get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
get userProfile() {
|
||||
return this.connection.userProfile;
|
||||
}
|
||||
get following() {
|
||||
return this.connection.following;
|
||||
}
|
||||
get muting() {
|
||||
return this.connection.muting;
|
||||
}
|
||||
get renoteMuting() {
|
||||
return this.connection.renoteMuting;
|
||||
}
|
||||
get blocking() {
|
||||
return this.connection.blocking;
|
||||
}
|
||||
get hidden() {
|
||||
return this.connection.hidden;
|
||||
}
|
||||
get subscriber() {
|
||||
return this.connection.subscriber;
|
||||
}
|
||||
constructor(connection, name){
|
||||
this.chName = name;
|
||||
this.connection = connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { MastodonStream } from "../channel.js";
|
||||
import { NoteConverter } from "../../converters/note.js";
|
||||
import { NoteHelpers } from "../../helpers/note.js";
|
||||
export class MastodonStreamDirect extends MastodonStream {
|
||||
static shouldShare = true;
|
||||
static requireCredential = true;
|
||||
static requiredScopes = [
|
||||
'read:statuses'
|
||||
];
|
||||
constructor(connection, name){
|
||||
super(connection, name);
|
||||
this.onNote = this.onNote.bind(this);
|
||||
this.onNoteEvent = this.onNoteEvent.bind(this);
|
||||
}
|
||||
get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
async init() {
|
||||
this.subscriber.on("notesStream", this.onNote);
|
||||
this.subscriber.on("noteUpdatesStream", this.onNoteEvent);
|
||||
}
|
||||
async onNote(note) {
|
||||
if (!this.shouldProcessNote(note)) return;
|
||||
NoteConverter.encodeEvent(note, this.user).then((encoded)=>{
|
||||
this.connection.send(this.chName, "update", encoded);
|
||||
});
|
||||
NoteHelpers.getConversationFromEvent(note.id, this.user).then((conversation)=>{
|
||||
this.connection.send(this.chName, "conversation", conversation);
|
||||
});
|
||||
}
|
||||
async onNoteEvent(data) {
|
||||
const note = data.body;
|
||||
if (!this.shouldProcessNote(note)) return;
|
||||
NoteHelpers.getConversationFromEvent(note.id, this.user).then((conversation)=>{
|
||||
this.connection.send(this.chName, "conversation", conversation);
|
||||
});
|
||||
switch(data.type){
|
||||
case "updated":
|
||||
NoteConverter.encodeEvent(note, this.user).then((encoded)=>{
|
||||
this.connection.send(this.chName, "status.update", encoded);
|
||||
});
|
||||
break;
|
||||
case "deleted":
|
||||
this.connection.send(this.chName, "delete", note.id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
shouldProcessNote(note) {
|
||||
if (note.visibility !== "specified") return false;
|
||||
if (note.userId !== this.user.id && !note.visibleUserIds?.includes(this.user.id)) return false;
|
||||
return true;
|
||||
}
|
||||
dispose() {
|
||||
this.subscriber.off("notesStream", this.onNote);
|
||||
this.subscriber.off("noteUpdatesStream", this.onNoteEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { MastodonStream } from "../channel.js";
|
||||
import { NoteConverter } from "../../converters/note.js";
|
||||
import { UserListJoinings } from "../../../../../models/index.js";
|
||||
export class MastodonStreamList extends MastodonStream {
|
||||
static shouldShare = false;
|
||||
static requireCredential = true;
|
||||
static requiredScopes = [
|
||||
'read:statuses'
|
||||
];
|
||||
listId;
|
||||
listUsers = [];
|
||||
listUsersClock;
|
||||
constructor(connection, name, list){
|
||||
super(connection, name);
|
||||
this.listId = list;
|
||||
this.onNote = this.onNote.bind(this);
|
||||
this.onNoteEvent = this.onNoteEvent.bind(this);
|
||||
this.updateListUsers = this.updateListUsers.bind(this);
|
||||
}
|
||||
get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
async init() {
|
||||
if (!this.listId) return;
|
||||
this.subscriber.on("notesStream", this.onNote);
|
||||
this.subscriber.on("noteUpdatesStream", this.onNoteEvent);
|
||||
this.updateListUsers();
|
||||
this.listUsersClock = setInterval(this.updateListUsers, 5000);
|
||||
}
|
||||
async updateListUsers() {
|
||||
const users = await UserListJoinings.find({
|
||||
where: {
|
||||
userListId: this.listId
|
||||
},
|
||||
select: [
|
||||
"userId"
|
||||
]
|
||||
});
|
||||
this.listUsers = users.map((x)=>x.userId);
|
||||
}
|
||||
async onNote(note) {
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'home');
|
||||
this.connection.send(this.chName, "update", encoded);
|
||||
}
|
||||
async onNoteEvent(data) {
|
||||
const note = data.body;
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
switch(data.type){
|
||||
case "updated":
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'home');
|
||||
this.connection.send(this.chName, "status.update", encoded);
|
||||
break;
|
||||
case "deleted":
|
||||
this.connection.send(this.chName, "delete", note.id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async shouldProcessNote(note) {
|
||||
if (!this.listUsers.includes(note.userId)) return false;
|
||||
if (note.channelId) return false;
|
||||
if (note.renoteId !== null && !note.text && this.renoteMuting.has(note.userId)) return false;
|
||||
if (note.visibility === "specified") return !!note.visibleUserIds?.includes(this.user.id);
|
||||
if (note.visibility === "followers") return this.following.has(note.userId);
|
||||
return true;
|
||||
}
|
||||
dispose() {
|
||||
this.subscriber.off("notesStream", this.onNote);
|
||||
this.subscriber.off("noteUpdatesStream", this.onNoteEvent);
|
||||
clearInterval(this.listUsersClock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { MastodonStream } from "../channel.js";
|
||||
import { isUserRelated } from "../../../../../misc/is-user-related.js";
|
||||
import { isInstanceMuted } from "../../../../../misc/is-instance-muted.js";
|
||||
import { NoteConverter } from "../../converters/note.js";
|
||||
import { fetchMeta } from "../../../../../misc/fetch-meta.js";
|
||||
import isQuote from "../../../../../misc/is-quote.js";
|
||||
export class MastodonStreamPublic extends MastodonStream {
|
||||
static shouldShare = true;
|
||||
static requireCredential = false;
|
||||
mediaOnly;
|
||||
localOnly;
|
||||
remoteOnly;
|
||||
allowLocalOnly;
|
||||
constructor(connection, name){
|
||||
super(connection, name);
|
||||
this.mediaOnly = name.endsWith(":media");
|
||||
this.localOnly = name.startsWith("public:local");
|
||||
this.remoteOnly = name.startsWith("public:remote");
|
||||
this.allowLocalOnly = name.startsWith("public:allow_local_only");
|
||||
this.onNote = this.onNote.bind(this);
|
||||
this.onNoteEvent = this.onNoteEvent.bind(this);
|
||||
}
|
||||
async init() {
|
||||
const meta = await fetchMeta();
|
||||
if (meta.disableGlobalTimeline) {
|
||||
if (this.user == null || !(this.user.isAdmin || this.user.isModerator)) return;
|
||||
}
|
||||
this.subscriber.on("notesStream", this.onNote);
|
||||
this.subscriber.on("noteUpdatesStream", this.onNoteEvent);
|
||||
}
|
||||
async onNote(note) {
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'public');
|
||||
this.connection.send(this.chName, "update", encoded);
|
||||
}
|
||||
async onNoteEvent(data) {
|
||||
const note = data.body;
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
switch(data.type){
|
||||
case "updated":
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'public');
|
||||
this.connection.send(this.chName, "status.update", encoded);
|
||||
break;
|
||||
case "deleted":
|
||||
this.connection.send(this.chName, "delete", note.id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async shouldProcessNote(note) {
|
||||
if (note.visibility !== "public") return false;
|
||||
if (note.channelId != null) return false;
|
||||
if (this.mediaOnly && note.fileIds.length < 1) return false;
|
||||
if (this.localOnly && note.userHost !== null) return false;
|
||||
if (this.remoteOnly && note.userHost === null) return false;
|
||||
if (note.localOnly && !this.allowLocalOnly && !this.localOnly) return false;
|
||||
if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return false;
|
||||
if (isUserRelated(note, this.muting)) return false;
|
||||
if (isUserRelated(note, this.blocking)) return false;
|
||||
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
||||
return true;
|
||||
}
|
||||
dispose() {
|
||||
this.subscriber.off("notesStream", this.onNote);
|
||||
this.subscriber.off("noteUpdatesStream", this.onNoteEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { MastodonStream } from "../channel.js";
|
||||
import { isUserRelated } from "../../../../../misc/is-user-related.js";
|
||||
import { isInstanceMuted } from "../../../../../misc/is-instance-muted.js";
|
||||
import { NoteConverter } from "../../converters/note.js";
|
||||
import isQuote from "../../../../../misc/is-quote.js";
|
||||
export class MastodonStreamTag extends MastodonStream {
|
||||
static shouldShare = false;
|
||||
static requireCredential = false;
|
||||
localOnly;
|
||||
tag;
|
||||
constructor(connection, name, tag){
|
||||
super(connection, name);
|
||||
this.tag = tag;
|
||||
this.localOnly = name.startsWith("hashtag:local");
|
||||
this.onNote = this.onNote.bind(this);
|
||||
this.onNoteEvent = this.onNoteEvent.bind(this);
|
||||
}
|
||||
get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
async init() {
|
||||
if (!this.tag) return;
|
||||
this.subscriber.on("notesStream", this.onNote);
|
||||
this.subscriber.on("noteUpdatesStream", this.onNoteEvent);
|
||||
}
|
||||
async onNote(note) {
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'public');
|
||||
this.connection.send(this.chName, "update", encoded);
|
||||
}
|
||||
async onNoteEvent(data) {
|
||||
const note = data.body;
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
switch(data.type){
|
||||
case "updated":
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'public');
|
||||
this.connection.send(this.chName, "status.update", encoded);
|
||||
break;
|
||||
case "deleted":
|
||||
this.connection.send(this.chName, "delete", note.id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async shouldProcessNote(note) {
|
||||
if (note.visibility !== "public") return false;
|
||||
if (note.channelId != null) return false;
|
||||
if (this.localOnly && note.userHost !== null) return false;
|
||||
if (!note.tags?.includes(this.tag)) return false;
|
||||
if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return false;
|
||||
if (isUserRelated(note, this.muting)) return false;
|
||||
if (isUserRelated(note, this.blocking)) return false;
|
||||
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
||||
return true;
|
||||
}
|
||||
dispose() {
|
||||
this.subscriber.off("notesStream", this.onNote);
|
||||
this.subscriber.off("noteUpdatesStream", this.onNoteEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { MastodonStream } from "../channel.js";
|
||||
import { isUserRelated } from "../../../../../misc/is-user-related.js";
|
||||
import { isInstanceMuted } from "../../../../../misc/is-instance-muted.js";
|
||||
import { NoteConverter } from "../../converters/note.js";
|
||||
import { NotificationConverter } from "../../converters/notification.js";
|
||||
import { AnnouncementConverter } from "../../converters/announcement.js";
|
||||
import isQuote from "../../../../../misc/is-quote.js";
|
||||
export class MastodonStreamUser extends MastodonStream {
|
||||
static shouldShare = true;
|
||||
static requireCredential = true;
|
||||
static requiredScopes = [
|
||||
'read:statuses',
|
||||
'read:notifications'
|
||||
];
|
||||
notificationsOnly;
|
||||
constructor(connection, name){
|
||||
super(connection, name);
|
||||
this.notificationsOnly = name === "user:notification";
|
||||
this.onNote = this.onNote.bind(this);
|
||||
this.onNoteEvent = this.onNoteEvent.bind(this);
|
||||
this.onUserEvent = this.onUserEvent.bind(this);
|
||||
this.onBroadcastEvent = this.onBroadcastEvent.bind(this);
|
||||
}
|
||||
get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
async init() {
|
||||
this.subscriber.on(`mainStream:${this.user.id}`, this.onUserEvent);
|
||||
if (!this.notificationsOnly) {
|
||||
this.subscriber.on("notesStream", this.onNote);
|
||||
this.subscriber.on("noteUpdatesStream", this.onNoteEvent);
|
||||
this.subscriber.on("broadcast", this.onBroadcastEvent);
|
||||
}
|
||||
}
|
||||
async onNote(note) {
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'home');
|
||||
this.connection.send(this.chName, "update", encoded);
|
||||
}
|
||||
async onNoteEvent(data) {
|
||||
const note = data.body;
|
||||
if (!await this.shouldProcessNote(note)) return;
|
||||
switch(data.type){
|
||||
case "updated":
|
||||
const encoded = await NoteConverter.encodeEvent(note, this.user, 'home');
|
||||
this.connection.send(this.chName, "status.update", encoded);
|
||||
break;
|
||||
case "deleted":
|
||||
this.connection.send(this.chName, "delete", note.id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async onUserEvent(data) {
|
||||
switch(data.type){
|
||||
case "notification":
|
||||
const encoded = await NotificationConverter.encodeEvent(data.body.id, this.user, 'notifications');
|
||||
if (encoded) this.connection.send(this.chName, "notification", encoded);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async onBroadcastEvent(data) {
|
||||
switch(data.type){
|
||||
case "announcementAdded":
|
||||
// This shouldn't be necessary but is for some reason
|
||||
data.body.createdAt = new Date(data.body.createdAt);
|
||||
this.connection.send(this.chName, "announcement", await AnnouncementConverter.encode(data.body, false));
|
||||
break;
|
||||
case "announcementDeleted":
|
||||
this.connection.send(this.chName, "announcement.delete", data.body);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async shouldProcessNote(note) {
|
||||
if (note.visibility === "hidden") return false;
|
||||
if (note.userId === this.user.id) return true;
|
||||
if (note.visibility === "specified") return note.visibleUserIds?.includes(this.user.id);
|
||||
if (note.channelId) return false;
|
||||
if (this.user.id !== note.userId && !this.following.has(note.userId)) return false;
|
||||
if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return false;
|
||||
if (isUserRelated(note, this.muting)) return false;
|
||||
if (isUserRelated(note, this.blocking)) return false;
|
||||
if (isUserRelated(note, this.hidden)) return false;
|
||||
if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false;
|
||||
return true;
|
||||
}
|
||||
dispose() {
|
||||
this.subscriber.off(`mainStream:${this.user.id}`, this.onUserEvent);
|
||||
if (!this.notificationsOnly) {
|
||||
this.subscriber.off("notesStream", this.onNote);
|
||||
this.subscriber.off("noteUpdatesStream", this.onNoteEvent);
|
||||
this.subscriber.off("broadcast", this.onBroadcastEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
import { Blockings, Followings, Mutings, RenoteMutings, UserListJoinings, UserProfiles } from "../../../../models/index.js";
|
||||
import { apiLogger } from "../../logger.js";
|
||||
import { MastodonStreamUser } from "./channels/user.js";
|
||||
import { MastodonStreamDirect } from "./channels/direct.js";
|
||||
import { MastodonStreamPublic } from "./channels/public.js";
|
||||
import { MastodonStreamList } from "./channels/list.js";
|
||||
import { toSingleLast } from "../../../../prelude/array.js";
|
||||
import { MastodonStreamTag } from "./channels/tag.js";
|
||||
const logger = apiLogger.createSubLogger("streaming").createSubLogger("mastodon");
|
||||
const channels = {
|
||||
"user": MastodonStreamUser,
|
||||
"user:notification": MastodonStreamUser,
|
||||
"direct": MastodonStreamDirect,
|
||||
"list": MastodonStreamList,
|
||||
"public": MastodonStreamPublic,
|
||||
"public:media": MastodonStreamPublic,
|
||||
"public:local": MastodonStreamPublic,
|
||||
"public:local:media": MastodonStreamPublic,
|
||||
"public:remote": MastodonStreamPublic,
|
||||
"public:remote:media": MastodonStreamPublic,
|
||||
"public:allow_local_only": MastodonStreamPublic,
|
||||
"public:allow_local_only:media": MastodonStreamPublic,
|
||||
"hashtag": MastodonStreamTag,
|
||||
"hashtag:local": MastodonStreamTag
|
||||
};
|
||||
export class MastodonStreamingConnection {
|
||||
user;
|
||||
userProfile;
|
||||
following = new Set();
|
||||
muting = new Set();
|
||||
renoteMuting = new Set();
|
||||
blocking = new Set();
|
||||
hidden = new Set();
|
||||
token;
|
||||
wsConnection;
|
||||
channels = [];
|
||||
subscriber;
|
||||
constructor(wsConnection, subscriber, user, token, query){
|
||||
const channel = toSingleLast(query.stream);
|
||||
logger.debug(`New connection on channel: ${channel}`);
|
||||
this.wsConnection = wsConnection;
|
||||
this.subscriber = subscriber;
|
||||
if (user) this.user = user;
|
||||
if (token) this.token = token;
|
||||
this.onMessage = this.onMessage.bind(this);
|
||||
this.onUserEvent = this.onUserEvent.bind(this);
|
||||
this.wsConnection.on("message", this.onMessage);
|
||||
if (this.user) {
|
||||
this.updateFollowing();
|
||||
this.updateMuting();
|
||||
this.updateRenoteMuting();
|
||||
this.updateBlocking();
|
||||
this.updateHidden();
|
||||
this.updateUserProfile();
|
||||
this.subscriber.on(`user:${this.user.id}`, this.onUserEvent);
|
||||
}
|
||||
if (channel) {
|
||||
const list = toSingleLast(query.list);
|
||||
const tag = toSingleLast(query.tag);
|
||||
this.onMessage({
|
||||
type: "utf8",
|
||||
utf8Data: JSON.stringify({
|
||||
stream: channel,
|
||||
type: "subscribe",
|
||||
list,
|
||||
tag
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
onUserEvent(data) {
|
||||
switch(data.type){
|
||||
case "follow":
|
||||
this.following.add(data.body.id);
|
||||
break;
|
||||
case "unfollow":
|
||||
this.following.delete(data.body.id);
|
||||
break;
|
||||
case "mute":
|
||||
this.muting.add(data.body.id);
|
||||
break;
|
||||
case "unmute":
|
||||
this.muting.delete(data.body.id);
|
||||
break;
|
||||
case "userHidden":
|
||||
this.hidden.add(data.body);
|
||||
break;
|
||||
case "userUnhidden":
|
||||
this.hidden.delete(data.body);
|
||||
break;
|
||||
// TODO: renote mute events
|
||||
// TODO: block events
|
||||
case "updateUserProfile":
|
||||
this.userProfile = data.body;
|
||||
break;
|
||||
case "terminate":
|
||||
this.closeConnection();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
async onMessage(data) {
|
||||
if (data.type !== "utf8") return;
|
||||
if (data.utf8Data == null) return;
|
||||
let message;
|
||||
try {
|
||||
message = JSON.parse(data.utf8Data);
|
||||
} catch (e) {
|
||||
logger.error("Failed to parse json data, ignoring");
|
||||
return;
|
||||
}
|
||||
const { stream, type, list, tag } = message;
|
||||
if (!message.stream || !message.type) {
|
||||
logger.error("Invalid message received, ignoring");
|
||||
return;
|
||||
}
|
||||
if (list ?? tag) logger.info(`${type}: ${stream} ${list ?? tag}`);
|
||||
else logger.info(`${type}: ${stream}`);
|
||||
switch(type){
|
||||
case "subscribe":
|
||||
this.connectChannel(stream, list, tag);
|
||||
break;
|
||||
case "unsubscribe":
|
||||
this.disconnectChannel(stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
send(stream, event, payload) {
|
||||
const json = JSON.stringify({
|
||||
stream: [
|
||||
stream
|
||||
],
|
||||
event: event,
|
||||
payload: typeof payload === "string" ? payload : JSON.stringify(payload)
|
||||
});
|
||||
this.wsConnection.send(json);
|
||||
}
|
||||
connectChannel(channel, list, tag) {
|
||||
if (!channels[channel]) {
|
||||
logger.info(`Ignoring connection to unknown channel ${channel}`);
|
||||
return;
|
||||
}
|
||||
if (channels[channel].requireCredential) {
|
||||
if (this.user == null) {
|
||||
logger.info(`Refusing connection to channel ${channel} without authentication, terminating connection`);
|
||||
this.closeConnection();
|
||||
return;
|
||||
} else if (!channels[channel].requiredScopes.every((p)=>this.token?.scopes?.includes(p))) {
|
||||
logger.info(`Refusing connection to channel ${channel} without required OAuth scopes, terminating connection`);
|
||||
this.closeConnection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (channels[channel].shouldShare && this.channels.some((c)=>c.chName === channel)) {
|
||||
return;
|
||||
}
|
||||
let ch;
|
||||
if (channel === "list") {
|
||||
ch = new channels[channel](this, channel, list);
|
||||
} else if (channel.startsWith("hashtag")) ch = new channels[channel](this, channel, tag);
|
||||
else ch = new channels[channel](this, channel);
|
||||
this.channels.push(ch);
|
||||
ch.init(null);
|
||||
}
|
||||
disconnectChannel(channelName) {
|
||||
const channel = this.channels.find((c)=>c.chName === channelName);
|
||||
if (channel) {
|
||||
if (channel.dispose) channel.dispose();
|
||||
this.channels = this.channels.filter((c)=>c.chName !== channelName);
|
||||
}
|
||||
}
|
||||
async updateFollowing() {
|
||||
const followings = await Followings.find({
|
||||
where: {
|
||||
followerId: this.user.id
|
||||
},
|
||||
select: [
|
||||
"followeeId"
|
||||
]
|
||||
});
|
||||
this.following = new Set(followings.map((x)=>x.followeeId));
|
||||
}
|
||||
async updateMuting() {
|
||||
const mutings = await Mutings.find({
|
||||
where: {
|
||||
muterId: this.user.id
|
||||
},
|
||||
select: [
|
||||
"muteeId"
|
||||
]
|
||||
});
|
||||
this.muting = new Set(mutings.map((x)=>x.muteeId));
|
||||
}
|
||||
async updateRenoteMuting() {
|
||||
const renoteMutings = await RenoteMutings.find({
|
||||
where: {
|
||||
muterId: this.user.id
|
||||
},
|
||||
select: [
|
||||
"muteeId"
|
||||
]
|
||||
});
|
||||
this.renoteMuting = new Set(renoteMutings.map((x)=>x.muteeId));
|
||||
}
|
||||
async updateBlocking() {
|
||||
const blockings = await Blockings.find({
|
||||
where: {
|
||||
blockeeId: this.user.id
|
||||
},
|
||||
select: [
|
||||
"blockerId"
|
||||
]
|
||||
});
|
||||
this.blocking = new Set(blockings.map((x)=>x.blockerId));
|
||||
}
|
||||
async updateHidden() {
|
||||
const hidden = await UserListJoinings.find({
|
||||
where: {
|
||||
userList: {
|
||||
userId: this.user.id,
|
||||
hideFromHomeTl: true
|
||||
}
|
||||
},
|
||||
select: [
|
||||
"userId"
|
||||
]
|
||||
});
|
||||
this.hidden = new Set(hidden.map((x)=>x.userId));
|
||||
}
|
||||
async updateUserProfile() {
|
||||
this.userProfile = await UserProfiles.findOneBy({
|
||||
userId: this.user.id
|
||||
});
|
||||
}
|
||||
closeConnection() {
|
||||
this.wsConnection.close();
|
||||
this.dispose();
|
||||
}
|
||||
dispose() {
|
||||
for (const c of this.channels.filter((c)=>c.dispose)){
|
||||
if (c.dispose) c.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user