Fixed 267U.pre2

This commit is contained in:
2026-07-26 18:25:37 +09:00
parent 50bfaeafdf
commit 317d00a284
1286 changed files with 80222 additions and 1 deletions
@@ -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);
}
}
}