Removed Mastodon API and messaging
This commit is contained in:
parent
c398acc9e5
commit
12f0bd4e2d
|
@ -36,8 +36,8 @@
|
||||||
"chokidar": "^3.3.1"
|
"chokidar": "^3.3.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bull-board/api": "^4.10.2",
|
"@bull-board/api": "^4.12.2",
|
||||||
"@bull-board/ui": "^4.10.2",
|
"@bull-board/ui": "^4.12.2",
|
||||||
"@tensorflow/tfjs": "^3.21.0",
|
"@tensorflow/tfjs": "^3.21.0",
|
||||||
"calckey-js": "^0.0.20",
|
"calckey-js": "^0.0.20",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
|
|
|
@ -79,7 +79,6 @@
|
||||||
"koa-send": "5.0.1",
|
"koa-send": "5.0.1",
|
||||||
"koa-slow": "2.1.0",
|
"koa-slow": "2.1.0",
|
||||||
"koa-views": "7.0.2",
|
"koa-views": "7.0.2",
|
||||||
"@cutls/megalodon": "5.1.15",
|
|
||||||
"mfm-js": "0.23.2",
|
"mfm-js": "0.23.2",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"multer": "1.4.4-lts.1",
|
"multer": "1.4.4-lts.1",
|
||||||
|
|
|
@ -34,7 +34,6 @@ import { Hashtag } from "@/models/entities/hashtag.js";
|
||||||
import { NoteFavorite } from "@/models/entities/note-favorite.js";
|
import { NoteFavorite } from "@/models/entities/note-favorite.js";
|
||||||
import { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
import { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
||||||
import { RegistrationTicket } from "@/models/entities/registration-tickets.js";
|
import { RegistrationTicket } from "@/models/entities/registration-tickets.js";
|
||||||
import { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import { Signin } from "@/models/entities/signin.js";
|
import { Signin } from "@/models/entities/signin.js";
|
||||||
import { AuthSession } from "@/models/entities/auth-session.js";
|
import { AuthSession } from "@/models/entities/auth-session.js";
|
||||||
import { FollowRequest } from "@/models/entities/follow-request.js";
|
import { FollowRequest } from "@/models/entities/follow-request.js";
|
||||||
|
@ -157,7 +156,6 @@ export const entities = [
|
||||||
SwSubscription,
|
SwSubscription,
|
||||||
AbuseUserReport,
|
AbuseUserReport,
|
||||||
RegistrationTicket,
|
RegistrationTicket,
|
||||||
MessagingMessage,
|
|
||||||
Signin,
|
Signin,
|
||||||
ModerationLog,
|
ModerationLog,
|
||||||
Clip,
|
Clip,
|
||||||
|
|
|
@ -9,8 +9,6 @@ export const kinds = [
|
||||||
"write:favorites",
|
"write:favorites",
|
||||||
"read:following",
|
"read:following",
|
||||||
"write:following",
|
"write:following",
|
||||||
"read:messaging",
|
|
||||||
"write:messaging",
|
|
||||||
"read:mutes",
|
"read:mutes",
|
||||||
"write:mutes",
|
"write:mutes",
|
||||||
"write:notes",
|
"write:notes",
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
import { packedNoteSchema } from "@/models/schema/note.js";
|
import { packedNoteSchema } from "@/models/schema/note.js";
|
||||||
import { packedUserListSchema } from "@/models/schema/user-list.js";
|
import { packedUserListSchema } from "@/models/schema/user-list.js";
|
||||||
import { packedAppSchema } from "@/models/schema/app.js";
|
import { packedAppSchema } from "@/models/schema/app.js";
|
||||||
import { packedMessagingMessageSchema } from "@/models/schema/messaging-message.js";
|
|
||||||
import { packedNotificationSchema } from "@/models/schema/notification.js";
|
import { packedNotificationSchema } from "@/models/schema/notification.js";
|
||||||
import { packedDriveFileSchema } from "@/models/schema/drive-file.js";
|
import { packedDriveFileSchema } from "@/models/schema/drive-file.js";
|
||||||
import { packedDriveFolderSchema } from "@/models/schema/drive-folder.js";
|
import { packedDriveFolderSchema } from "@/models/schema/drive-folder.js";
|
||||||
|
@ -42,7 +41,6 @@ export const refs = {
|
||||||
UserList: packedUserListSchema,
|
UserList: packedUserListSchema,
|
||||||
UserGroup: packedUserGroupSchema,
|
UserGroup: packedUserGroupSchema,
|
||||||
App: packedAppSchema,
|
App: packedAppSchema,
|
||||||
MessagingMessage: packedMessagingMessageSchema,
|
|
||||||
Note: packedNoteSchema,
|
Note: packedNoteSchema,
|
||||||
NoteReaction: packedNoteReactionSchema,
|
NoteReaction: packedNoteReactionSchema,
|
||||||
NoteFavorite: packedNoteFavoriteSchema,
|
NoteFavorite: packedNoteFavoriteSchema,
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
import {
|
|
||||||
PrimaryColumn,
|
|
||||||
Entity,
|
|
||||||
Index,
|
|
||||||
JoinColumn,
|
|
||||||
Column,
|
|
||||||
ManyToOne,
|
|
||||||
} from "typeorm";
|
|
||||||
import { User } from "./user.js";
|
|
||||||
import { DriveFile } from "./drive-file.js";
|
|
||||||
import { id } from "../id.js";
|
|
||||||
import { UserGroup } from "./user-group.js";
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class MessagingMessage {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column('timestamp with time zone', {
|
|
||||||
comment: 'The created date of the MessagingMessage.',
|
|
||||||
})
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The sender user ID.',
|
|
||||||
})
|
|
||||||
public userId: User["id"];
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(), nullable: true,
|
|
||||||
comment: 'The recipient user ID.',
|
|
||||||
})
|
|
||||||
public recipientId: User["id"] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public recipient: User | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(), nullable: true,
|
|
||||||
comment: 'The recipient group ID.',
|
|
||||||
})
|
|
||||||
public groupId: UserGroup["id"] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => UserGroup, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public group: UserGroup | null;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 4096, nullable: true,
|
|
||||||
})
|
|
||||||
public text: string | null;
|
|
||||||
|
|
||||||
@Column('boolean', {
|
|
||||||
default: false,
|
|
||||||
})
|
|
||||||
public isRead: boolean;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 512, nullable: true,
|
|
||||||
})
|
|
||||||
public uri: string | null;
|
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
array: true, default: '{}',
|
|
||||||
})
|
|
||||||
public reads: User["id"][];
|
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public fileId: DriveFile["id"] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => DriveFile, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public file: DriveFile | null;
|
|
||||||
}
|
|
|
@ -19,7 +19,6 @@ import { DriveFolderRepository } from "./repositories/drive-folder.js";
|
||||||
import { AccessToken } from "./entities/access-token.js";
|
import { AccessToken } from "./entities/access-token.js";
|
||||||
import { UserNotePining } from "./entities/user-note-pining.js";
|
import { UserNotePining } from "./entities/user-note-pining.js";
|
||||||
import { SigninRepository } from "./repositories/signin.js";
|
import { SigninRepository } from "./repositories/signin.js";
|
||||||
import { MessagingMessageRepository } from "./repositories/messaging-message.js";
|
|
||||||
import { UserListRepository } from "./repositories/user-list.js";
|
import { UserListRepository } from "./repositories/user-list.js";
|
||||||
import { UserListJoining } from "./entities/user-list-joining.js";
|
import { UserListJoining } from "./entities/user-list-joining.js";
|
||||||
import { UserGroupRepository } from "./repositories/user-group.js";
|
import { UserGroupRepository } from "./repositories/user-group.js";
|
||||||
|
@ -110,7 +109,6 @@ export const RegistrationTickets = db.getRepository(RegistrationTicket);
|
||||||
export const AuthSessions = AuthSessionRepository;
|
export const AuthSessions = AuthSessionRepository;
|
||||||
export const AccessTokens = db.getRepository(AccessToken);
|
export const AccessTokens = db.getRepository(AccessToken);
|
||||||
export const Signins = SigninRepository;
|
export const Signins = SigninRepository;
|
||||||
export const MessagingMessages = MessagingMessageRepository;
|
|
||||||
export const Pages = PageRepository;
|
export const Pages = PageRepository;
|
||||||
export const PageLikes = PageLikeRepository;
|
export const PageLikes = PageLikeRepository;
|
||||||
export const GalleryPosts = GalleryPostRepository;
|
export const GalleryPosts = GalleryPostRepository;
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { db } from "@/db/postgre.js";
|
|
||||||
import { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import { Users, DriveFiles, UserGroups } from "../index.js";
|
|
||||||
import type { Packed } from "@/misc/schema.js";
|
|
||||||
import type { User } from "@/models/entities/user.js";
|
|
||||||
|
|
||||||
export const MessagingMessageRepository = db
|
|
||||||
.getRepository(MessagingMessage)
|
|
||||||
.extend({
|
|
||||||
async pack(
|
|
||||||
src: MessagingMessage["id"] | MessagingMessage,
|
|
||||||
me?: { id: User["id"] } | null | undefined,
|
|
||||||
options?: {
|
|
||||||
populateRecipient?: boolean;
|
|
||||||
populateGroup?: boolean;
|
|
||||||
},
|
|
||||||
): Promise<Packed<"MessagingMessage">> {
|
|
||||||
const opts = options || {
|
|
||||||
populateRecipient: true,
|
|
||||||
populateGroup: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const message =
|
|
||||||
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: message.id,
|
|
||||||
createdAt: message.createdAt.toISOString(),
|
|
||||||
text: message.text,
|
|
||||||
userId: message.userId,
|
|
||||||
user: await Users.pack(message.user || message.userId, me),
|
|
||||||
recipientId: message.recipientId,
|
|
||||||
recipient:
|
|
||||||
message.recipientId && opts.populateRecipient
|
|
||||||
? await Users.pack(message.recipient || message.recipientId, me)
|
|
||||||
: undefined,
|
|
||||||
groupId: message.groupId,
|
|
||||||
group:
|
|
||||||
message.groupId && opts.populateGroup
|
|
||||||
? await UserGroups.pack(message.group || message.groupId)
|
|
||||||
: undefined,
|
|
||||||
fileId: message.fileId,
|
|
||||||
file: message.fileId ? await DriveFiles.pack(message.fileId) : null,
|
|
||||||
isRead: message.isRead,
|
|
||||||
reads: message.reads,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -26,7 +26,6 @@ import {
|
||||||
Followings,
|
Followings,
|
||||||
FollowRequests,
|
FollowRequests,
|
||||||
Instances,
|
Instances,
|
||||||
MessagingMessages,
|
|
||||||
Mutings,
|
Mutings,
|
||||||
Notes,
|
Notes,
|
||||||
NoteUnreads,
|
NoteUnreads,
|
||||||
|
@ -174,42 +173,6 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async getHasUnreadMessagingMessage(userId: User["id"]): Promise<boolean> {
|
|
||||||
const mute = await Mutings.findBy({
|
|
||||||
muterId: userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const joinings = await UserGroupJoinings.findBy({ userId: userId });
|
|
||||||
|
|
||||||
const groupQs = Promise.all(
|
|
||||||
joinings.map((j) =>
|
|
||||||
MessagingMessages.createQueryBuilder("message")
|
|
||||||
.where("message.groupId = :groupId", { groupId: j.userGroupId })
|
|
||||||
.andWhere("message.userId != :userId", { userId: userId })
|
|
||||||
.andWhere("NOT (:userId = ANY(message.reads))", { userId: userId })
|
|
||||||
.andWhere("message.createdAt > :joinedAt", { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない
|
|
||||||
.getOne()
|
|
||||||
.then((x) => x != null),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [withUser, withGroups] = await Promise.all([
|
|
||||||
MessagingMessages.count({
|
|
||||||
where: {
|
|
||||||
recipientId: userId,
|
|
||||||
isRead: false,
|
|
||||||
...(mute.length > 0
|
|
||||||
? { userId: Not(In(mute.map((x) => x.muteeId))) }
|
|
||||||
: {}),
|
|
||||||
},
|
|
||||||
take: 1,
|
|
||||||
}).then((count) => count > 0),
|
|
||||||
groupQs,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return withUser || withGroups.some((x) => x);
|
|
||||||
},
|
|
||||||
|
|
||||||
async getHasUnreadAnnouncement(userId: User["id"]): Promise<boolean> {
|
async getHasUnreadAnnouncement(userId: User["id"]): Promise<boolean> {
|
||||||
const reads = await AnnouncementReads.findBy({
|
const reads = await AnnouncementReads.findBy({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
|
@ -540,9 +503,6 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id),
|
hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id),
|
||||||
hasUnreadAntenna: this.getHasUnreadAntenna(user.id),
|
hasUnreadAntenna: this.getHasUnreadAntenna(user.id),
|
||||||
hasUnreadChannel: this.getHasUnreadChannel(user.id),
|
hasUnreadChannel: this.getHasUnreadChannel(user.id),
|
||||||
hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(
|
|
||||||
user.id,
|
|
||||||
),
|
|
||||||
hasUnreadNotification: this.getHasUnreadNotification(user.id),
|
hasUnreadNotification: this.getHasUnreadNotification(user.id),
|
||||||
hasPendingReceivedFollowRequest:
|
hasPendingReceivedFollowRequest:
|
||||||
this.getHasPendingReceivedFollowRequest(user.id),
|
this.getHasPendingReceivedFollowRequest(user.id),
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
export const packedMessagingMessageSchema = {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
id: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
format: "id",
|
|
||||||
example: "xxxxxxxxxx",
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
format: "date-time",
|
|
||||||
},
|
|
||||||
userId: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
type: "object",
|
|
||||||
ref: "UserLite",
|
|
||||||
optional: true,
|
|
||||||
nullable: false,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
fileId: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
type: "object",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
ref: "DriveFile",
|
|
||||||
},
|
|
||||||
recipientId: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: true,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
recipient: {
|
|
||||||
type: "object",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
ref: "UserLite",
|
|
||||||
},
|
|
||||||
groupId: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: true,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
group: {
|
|
||||||
type: "object",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
ref: "UserGroup",
|
|
||||||
},
|
|
||||||
isRead: {
|
|
||||||
type: "boolean",
|
|
||||||
optional: true,
|
|
||||||
nullable: false,
|
|
||||||
},
|
|
||||||
reads: {
|
|
||||||
type: "array",
|
|
||||||
optional: true,
|
|
||||||
nullable: false,
|
|
||||||
items: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
format: "id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
|
@ -424,11 +424,6 @@ export const packedMeDetailedOnlySchema = {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
hasUnreadMessagingMessage: {
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
hasUnreadNotification: {
|
hasUnreadNotification: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
|
|
@ -5,14 +5,11 @@ import type {
|
||||||
CacheableRemoteUser,
|
CacheableRemoteUser,
|
||||||
CacheableUser,
|
CacheableUser,
|
||||||
} from "@/models/entities/user.js";
|
} from "@/models/entities/user.js";
|
||||||
import { User, IRemoteUser } from "@/models/entities/user.js";
|
|
||||||
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import {
|
import {
|
||||||
Notes,
|
Notes,
|
||||||
Users,
|
Users,
|
||||||
UserPublickeys,
|
UserPublickeys,
|
||||||
MessagingMessages,
|
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import { Cache } from "@/misc/cache.js";
|
import { Cache } from "@/misc/cache.js";
|
||||||
import { uriPersonCache, userByIdCache } from "@/services/user-cache.js";
|
import { uriPersonCache, userByIdCache } from "@/services/user-cache.js";
|
||||||
|
@ -88,24 +85,6 @@ export default class DbResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getMessageFromApId(
|
|
||||||
value: string | IObject,
|
|
||||||
): Promise<MessagingMessage | null> {
|
|
||||||
const parsed = parseUri(value);
|
|
||||||
|
|
||||||
if (parsed.local) {
|
|
||||||
if (parsed.type !== "notes") return null;
|
|
||||||
|
|
||||||
return await MessagingMessages.findOneBy({
|
|
||||||
id: parsed.id,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return await MessagingMessages.findOneBy({
|
|
||||||
uri: parsed.uri,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AP Person => Misskey User in DB
|
* AP Person => Misskey User in DB
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,7 +3,6 @@ import deleteNode from "@/services/note/delete.js";
|
||||||
import { apLogger } from "../../logger.js";
|
import { apLogger } from "../../logger.js";
|
||||||
import DbResolver from "../../db-resolver.js";
|
import DbResolver from "../../db-resolver.js";
|
||||||
import { getApLock } from "@/misc/app-lock.js";
|
import { getApLock } from "@/misc/app-lock.js";
|
||||||
import { deleteMessage } from "@/services/messages/delete.js";
|
|
||||||
|
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
|
@ -20,16 +19,7 @@ export default async function (
|
||||||
const note = await dbResolver.getNoteFromApId(uri);
|
const note = await dbResolver.getNoteFromApId(uri);
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
const message = await dbResolver.getMessageFromApId(uri);
|
return "ok: note is null";
|
||||||
if (message == null) return "message not found";
|
|
||||||
|
|
||||||
if (message.userId !== actor.id) {
|
|
||||||
return "The user trying to delete the post is not the post author";
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteMessage(message);
|
|
||||||
|
|
||||||
return "ok: message deleted";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.userId !== actor.id) {
|
if (note.userId !== actor.id) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ import Resolver from "../resolver.js";
|
||||||
import create from "./create/index.js";
|
import create from "./create/index.js";
|
||||||
import performDeleteActivity from "./delete/index.js";
|
import performDeleteActivity from "./delete/index.js";
|
||||||
import performUpdateActivity from "./update/index.js";
|
import performUpdateActivity from "./update/index.js";
|
||||||
import { performReadActivity } from "./read.js";
|
|
||||||
import follow from "./follow.js";
|
import follow from "./follow.js";
|
||||||
import undo from "./undo/index.js";
|
import undo from "./undo/index.js";
|
||||||
import like from "./like.js";
|
import like from "./like.js";
|
||||||
|
@ -81,8 +80,6 @@ async function performOneActivity(
|
||||||
await performDeleteActivity(actor, activity);
|
await performDeleteActivity(actor, activity);
|
||||||
} else if (isUpdate(activity)) {
|
} else if (isUpdate(activity)) {
|
||||||
await performUpdateActivity(actor, activity);
|
await performUpdateActivity(actor, activity);
|
||||||
} else if (isRead(activity)) {
|
|
||||||
await performReadActivity(actor, activity);
|
|
||||||
} else if (isFollow(activity)) {
|
} else if (isFollow(activity)) {
|
||||||
await follow(actor, activity);
|
await follow(actor, activity);
|
||||||
} else if (isAccept(activity)) {
|
} else if (isAccept(activity)) {
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
|
||||||
import type { IRead } from "../type.js";
|
|
||||||
import { getApId } from "../type.js";
|
|
||||||
import { isSelfHost, extractDbHost } from "@/misc/convert-host.js";
|
|
||||||
import { MessagingMessages } from "@/models/index.js";
|
|
||||||
import { readUserMessagingMessage } from "../../../server/api/common/read-messaging-message.js";
|
|
||||||
|
|
||||||
export const performReadActivity = async (
|
|
||||||
actor: CacheableRemoteUser,
|
|
||||||
activity: IRead,
|
|
||||||
): Promise<string> => {
|
|
||||||
const id = await getApId(activity.object);
|
|
||||||
|
|
||||||
if (!isSelfHost(extractDbHost(id))) {
|
|
||||||
return `skip: Read to foreign host (${id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageId = id.split("/").pop();
|
|
||||||
|
|
||||||
const message = await MessagingMessages.findOneBy({ id: messageId });
|
|
||||||
if (message == null) {
|
|
||||||
return "skip: message not found";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actor.id !== message.recipientId) {
|
|
||||||
return "skip: actor is not a message recipient";
|
|
||||||
}
|
|
||||||
|
|
||||||
await readUserMessagingMessage(message.recipientId!, message.userId, [
|
|
||||||
message.id,
|
|
||||||
]);
|
|
||||||
return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`;
|
|
||||||
};
|
|
|
@ -15,7 +15,7 @@ import { apLogger } from "../logger.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import { deliverQuestionUpdate } from "@/services/note/polls/update.js";
|
import { deliverQuestionUpdate } from "@/services/note/polls/update.js";
|
||||||
import { extractDbHost, toPuny } from "@/misc/convert-host.js";
|
import { extractDbHost, toPuny } from "@/misc/convert-host.js";
|
||||||
import { Emojis, Polls, MessagingMessages } from "@/models/index.js";
|
import { Emojis, Polls } from "@/models/index.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import type { IObject, IPost } from "../type.js";
|
import type { IObject, IPost } from "../type.js";
|
||||||
import {
|
import {
|
||||||
|
@ -30,7 +30,6 @@ import type { Emoji } from "@/models/entities/emoji.js";
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from "@/misc/gen-id.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import { getApLock } from "@/misc/app-lock.js";
|
import { getApLock } from "@/misc/app-lock.js";
|
||||||
import { createMessage } from "@/services/messages/create.js";
|
|
||||||
import { parseAudience } from "../audience.js";
|
import { parseAudience } from "../audience.js";
|
||||||
import { extractApMentions } from "./mention.js";
|
import { extractApMentions } from "./mention.js";
|
||||||
import DbResolver from "../db-resolver.js";
|
import DbResolver from "../db-resolver.js";
|
||||||
|
@ -152,6 +151,10 @@ export async function createNote(
|
||||||
|
|
||||||
let isTalk = note._misskey_talk && visibility === "specified";
|
let isTalk = note._misskey_talk && visibility === "specified";
|
||||||
|
|
||||||
|
if (isTalk) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const apMentions = await extractApMentions(note.tag);
|
const apMentions = await extractApMentions(note.tag);
|
||||||
const apHashtags = await extractApHashtags(note.tag);
|
const apHashtags = await extractApHashtags(note.tag);
|
||||||
|
|
||||||
|
@ -190,17 +193,6 @@ export async function createNote(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(async (e) => {
|
.catch(async (e) => {
|
||||||
// トークだったらinReplyToのエラーは無視
|
|
||||||
const uri = getApId(note.inReplyTo);
|
|
||||||
if (uri.startsWith(`${config.url}/`)) {
|
|
||||||
const id = uri.split("/").pop();
|
|
||||||
const talk = await MessagingMessages.findOneBy({ id });
|
|
||||||
if (talk) {
|
|
||||||
isTalk = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Error in inReplyTo ${note.inReplyTo} - ${e.statusCode || e}`,
|
`Error in inReplyTo ${note.inReplyTo} - ${e.statusCode || e}`,
|
||||||
);
|
);
|
||||||
|
@ -323,20 +315,6 @@ export async function createNote(
|
||||||
() => undefined,
|
() => undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isTalk) {
|
|
||||||
for (const recipient of visibleUsers) {
|
|
||||||
await createMessage(
|
|
||||||
actor,
|
|
||||||
recipient,
|
|
||||||
undefined,
|
|
||||||
text || undefined,
|
|
||||||
files && files.length > 0 ? files[0] : null,
|
|
||||||
object.id,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await post(
|
return await post(
|
||||||
actor,
|
actor,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import config from "@/config/index.js";
|
|
||||||
import type { User } from "@/models/entities/user.js";
|
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
|
|
||||||
export const renderReadActivity = (
|
|
||||||
user: { id: User["id"] },
|
|
||||||
message: MessagingMessage,
|
|
||||||
) => ({
|
|
||||||
type: "Read",
|
|
||||||
actor: `${config.url}/users/${user.id}`,
|
|
||||||
object: message.uri,
|
|
||||||
});
|
|
|
@ -22,13 +22,14 @@ import Featured from "./activitypub/featured.js";
|
||||||
import Following from "./activitypub/following.js";
|
import Following from "./activitypub/following.js";
|
||||||
import Followers from "./activitypub/followers.js";
|
import Followers from "./activitypub/followers.js";
|
||||||
import Outbox, { packActivity } from "./activitypub/outbox.js";
|
import Outbox, { packActivity } from "./activitypub/outbox.js";
|
||||||
|
import {IActivity} from "@/remote/activitypub/type";
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
//#region Routing
|
//#region Routing
|
||||||
|
|
||||||
function inbox(ctx: Router.RouterContext) {
|
async function inbox(ctx: Router.RouterContext) {
|
||||||
let signature;
|
let signature;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -38,7 +39,7 @@ function inbox(ctx: Router.RouterContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
processInbox(ctx.request.body, signature);
|
processInbox(ctx.request.body as IActivity, signature);
|
||||||
|
|
||||||
ctx.status = 202;
|
ctx.status = 202;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
import {
|
|
||||||
publishMainStream,
|
|
||||||
publishGroupMessagingStream,
|
|
||||||
} from "@/services/stream.js";
|
|
||||||
import { publishMessagingStream } from "@/services/stream.js";
|
|
||||||
import { publishMessagingIndexStream } from "@/services/stream.js";
|
|
||||||
import { pushNotification } from "@/services/push-notification.js";
|
|
||||||
import type { User, IRemoteUser } from "@/models/entities/user.js";
|
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import { MessagingMessages, UserGroupJoinings, Users } from "@/models/index.js";
|
|
||||||
import { In } from "typeorm";
|
|
||||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
|
||||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
|
||||||
import { toArray } from "@/prelude/array.js";
|
|
||||||
import { renderReadActivity } from "@/remote/activitypub/renderer/read.js";
|
|
||||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
|
||||||
import { deliver } from "@/queue/index.js";
|
|
||||||
import orderedCollection from "@/remote/activitypub/renderer/ordered-collection.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark messages as read
|
|
||||||
*/
|
|
||||||
export async function readUserMessagingMessage(
|
|
||||||
userId: User["id"],
|
|
||||||
otherpartyId: User["id"],
|
|
||||||
messageIds: MessagingMessage["id"][],
|
|
||||||
) {
|
|
||||||
if (messageIds.length === 0) return;
|
|
||||||
|
|
||||||
const messages = await MessagingMessages.findBy({
|
|
||||||
id: In(messageIds),
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (message.recipientId !== userId) {
|
|
||||||
throw new IdentifiableError(
|
|
||||||
"e140a4bf-49ce-4fb6-b67c-b78dadf6b52f",
|
|
||||||
"Access denied (user).",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update documents
|
|
||||||
await MessagingMessages.update(
|
|
||||||
{
|
|
||||||
id: In(messageIds),
|
|
||||||
userId: otherpartyId,
|
|
||||||
recipientId: userId,
|
|
||||||
isRead: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isRead: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Publish event
|
|
||||||
publishMessagingStream(otherpartyId, userId, "read", messageIds);
|
|
||||||
publishMessagingIndexStream(userId, "read", messageIds);
|
|
||||||
|
|
||||||
if (!(await Users.getHasUnreadMessagingMessage(userId))) {
|
|
||||||
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
|
|
||||||
publishMainStream(userId, "readAllMessagingMessages");
|
|
||||||
pushNotification(userId, "readAllMessagingMessages", undefined);
|
|
||||||
} else {
|
|
||||||
// そのユーザーとのメッセージで未読がなければイベント発行
|
|
||||||
const count = await MessagingMessages.count({
|
|
||||||
where: {
|
|
||||||
userId: otherpartyId,
|
|
||||||
recipientId: userId,
|
|
||||||
isRead: false,
|
|
||||||
},
|
|
||||||
take: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!count) {
|
|
||||||
pushNotification(userId, "readAllMessagingMessagesOfARoom", {
|
|
||||||
userId: otherpartyId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark messages as read
|
|
||||||
*/
|
|
||||||
export async function readGroupMessagingMessage(
|
|
||||||
userId: User["id"],
|
|
||||||
groupId: UserGroup["id"],
|
|
||||||
messageIds: MessagingMessage["id"][],
|
|
||||||
) {
|
|
||||||
if (messageIds.length === 0) return;
|
|
||||||
|
|
||||||
// check joined
|
|
||||||
const joining = await UserGroupJoinings.findOneBy({
|
|
||||||
userId: userId,
|
|
||||||
userGroupId: groupId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
throw new IdentifiableError(
|
|
||||||
"930a270c-714a-46b2-b776-ad27276dc569",
|
|
||||||
"Access denied (group).",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = await MessagingMessages.findBy({
|
|
||||||
id: In(messageIds),
|
|
||||||
});
|
|
||||||
|
|
||||||
const reads: MessagingMessage["id"][] = [];
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (message.userId === userId) continue;
|
|
||||||
if (message.reads.includes(userId)) continue;
|
|
||||||
|
|
||||||
// Update document
|
|
||||||
await MessagingMessages.createQueryBuilder()
|
|
||||||
.update()
|
|
||||||
.set({
|
|
||||||
reads: (() => `array_append("reads", '${joining.userId}')`) as any,
|
|
||||||
})
|
|
||||||
.where("id = :id", { id: message.id })
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
reads.push(message.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish event
|
|
||||||
publishGroupMessagingStream(groupId, "read", {
|
|
||||||
ids: reads,
|
|
||||||
userId: userId,
|
|
||||||
});
|
|
||||||
publishMessagingIndexStream(userId, "read", reads);
|
|
||||||
|
|
||||||
if (!(await Users.getHasUnreadMessagingMessage(userId))) {
|
|
||||||
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
|
|
||||||
publishMainStream(userId, "readAllMessagingMessages");
|
|
||||||
pushNotification(userId, "readAllMessagingMessages", undefined);
|
|
||||||
} else {
|
|
||||||
// そのグループにおいて未読がなければイベント発行
|
|
||||||
const unreadExist = await MessagingMessages.createQueryBuilder("message")
|
|
||||||
.where("message.groupId = :groupId", { groupId: groupId })
|
|
||||||
.andWhere("message.userId != :userId", { userId: userId })
|
|
||||||
.andWhere("NOT (:userId = ANY(message.reads))", { userId: userId })
|
|
||||||
.andWhere("message.createdAt > :joinedAt", {
|
|
||||||
joinedAt: joining.createdAt,
|
|
||||||
}) // 自分が加入する前の会話については、未読扱いしない
|
|
||||||
.getOne()
|
|
||||||
.then((x) => x != null);
|
|
||||||
|
|
||||||
if (!unreadExist) {
|
|
||||||
pushNotification(userId, "readAllMessagingMessagesOfARoom", { groupId });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deliverReadActivity(
|
|
||||||
user: { id: User["id"]; host: null },
|
|
||||||
recipient: IRemoteUser,
|
|
||||||
messages: MessagingMessage | MessagingMessage[],
|
|
||||||
) {
|
|
||||||
messages = toArray(messages).filter((x) => x.uri);
|
|
||||||
const contents = messages.map((x) => renderReadActivity(user, x));
|
|
||||||
|
|
||||||
if (contents.length > 1) {
|
|
||||||
const collection = orderedCollection(
|
|
||||||
null,
|
|
||||||
contents.length,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
contents,
|
|
||||||
);
|
|
||||||
deliver(user, renderActivity(collection), recipient.inbox);
|
|
||||||
} else {
|
|
||||||
for (const content of contents) {
|
|
||||||
deliver(user, renderActivity(content), recipient.inbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -192,7 +192,6 @@ import * as ep___i_notifications from "./endpoints/i/notifications.js";
|
||||||
import * as ep___i_pageLikes from "./endpoints/i/page-likes.js";
|
import * as ep___i_pageLikes from "./endpoints/i/page-likes.js";
|
||||||
import * as ep___i_pages from "./endpoints/i/pages.js";
|
import * as ep___i_pages from "./endpoints/i/pages.js";
|
||||||
import * as ep___i_pin from "./endpoints/i/pin.js";
|
import * as ep___i_pin from "./endpoints/i/pin.js";
|
||||||
import * as ep___i_readAllMessagingMessages from "./endpoints/i/read-all-messaging-messages.js";
|
|
||||||
import * as ep___i_readAllUnreadNotes from "./endpoints/i/read-all-unread-notes.js";
|
import * as ep___i_readAllUnreadNotes from "./endpoints/i/read-all-unread-notes.js";
|
||||||
import * as ep___i_readAnnouncement from "./endpoints/i/read-announcement.js";
|
import * as ep___i_readAnnouncement from "./endpoints/i/read-announcement.js";
|
||||||
import * as ep___i_regenerateToken from "./endpoints/i/regenerate-token.js";
|
import * as ep___i_regenerateToken from "./endpoints/i/regenerate-token.js";
|
||||||
|
@ -216,11 +215,6 @@ import * as ep___i_webhooks_show from "./endpoints/i/webhooks/show.js";
|
||||||
import * as ep___i_webhooks_list from "./endpoints/i/webhooks/list.js";
|
import * as ep___i_webhooks_list from "./endpoints/i/webhooks/list.js";
|
||||||
import * as ep___i_webhooks_update from "./endpoints/i/webhooks/update.js";
|
import * as ep___i_webhooks_update from "./endpoints/i/webhooks/update.js";
|
||||||
import * as ep___i_webhooks_delete from "./endpoints/i/webhooks/delete.js";
|
import * as ep___i_webhooks_delete from "./endpoints/i/webhooks/delete.js";
|
||||||
import * as ep___messaging_history from "./endpoints/messaging/history.js";
|
|
||||||
import * as ep___messaging_messages from "./endpoints/messaging/messages.js";
|
|
||||||
import * as ep___messaging_messages_create from "./endpoints/messaging/messages/create.js";
|
|
||||||
import * as ep___messaging_messages_delete from "./endpoints/messaging/messages/delete.js";
|
|
||||||
import * as ep___messaging_messages_read from "./endpoints/messaging/messages/read.js";
|
|
||||||
import * as ep___meta from "./endpoints/meta.js";
|
import * as ep___meta from "./endpoints/meta.js";
|
||||||
import * as ep___miauth_genToken from "./endpoints/miauth/gen-token.js";
|
import * as ep___miauth_genToken from "./endpoints/miauth/gen-token.js";
|
||||||
import * as ep___mute_create from "./endpoints/mute/create.js";
|
import * as ep___mute_create from "./endpoints/mute/create.js";
|
||||||
|
@ -533,7 +527,6 @@ const eps = [
|
||||||
["i/page-likes", ep___i_pageLikes],
|
["i/page-likes", ep___i_pageLikes],
|
||||||
["i/pages", ep___i_pages],
|
["i/pages", ep___i_pages],
|
||||||
["i/pin", ep___i_pin],
|
["i/pin", ep___i_pin],
|
||||||
["i/read-all-messaging-messages", ep___i_readAllMessagingMessages],
|
|
||||||
["i/read-all-unread-notes", ep___i_readAllUnreadNotes],
|
["i/read-all-unread-notes", ep___i_readAllUnreadNotes],
|
||||||
["i/read-announcement", ep___i_readAnnouncement],
|
["i/read-announcement", ep___i_readAnnouncement],
|
||||||
["i/regenerate-token", ep___i_regenerateToken],
|
["i/regenerate-token", ep___i_regenerateToken],
|
||||||
|
@ -557,11 +550,6 @@ const eps = [
|
||||||
["i/webhooks/show", ep___i_webhooks_show],
|
["i/webhooks/show", ep___i_webhooks_show],
|
||||||
["i/webhooks/update", ep___i_webhooks_update],
|
["i/webhooks/update", ep___i_webhooks_update],
|
||||||
["i/webhooks/delete", ep___i_webhooks_delete],
|
["i/webhooks/delete", ep___i_webhooks_delete],
|
||||||
["messaging/history", ep___messaging_history],
|
|
||||||
["messaging/messages", ep___messaging_messages],
|
|
||||||
["messaging/messages/create", ep___messaging_messages_create],
|
|
||||||
["messaging/messages/delete", ep___messaging_messages_delete],
|
|
||||||
["messaging/messages/read", ep___messaging_messages_read],
|
|
||||||
["meta", ep___meta],
|
["meta", ep___meta],
|
||||||
["miauth/gen-token", ep___miauth_genToken],
|
["miauth/gen-token", ep___miauth_genToken],
|
||||||
["mute/create", ep___mute_create],
|
["mute/create", ep___mute_create],
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { publishMainStream } from "@/services/stream.js";
|
|
||||||
import define from "../../define.js";
|
|
||||||
import { MessagingMessages, UserGroupJoinings } from "@/models/index.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["account", "messaging"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "write:account",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {},
|
|
||||||
required: [],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
// Update documents
|
|
||||||
await MessagingMessages.update(
|
|
||||||
{
|
|
||||||
recipientId: user.id,
|
|
||||||
isRead: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isRead: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const joinings = await UserGroupJoinings.findBy({ userId: user.id });
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
joinings.map((j) =>
|
|
||||||
MessagingMessages.createQueryBuilder()
|
|
||||||
.update()
|
|
||||||
.set({
|
|
||||||
reads: (() => `array_append("reads", '${user.id}')`) as any,
|
|
||||||
})
|
|
||||||
.where("groupId = :groupId", { groupId: j.userGroupId })
|
|
||||||
.andWhere("userId != :userId", { userId: user.id })
|
|
||||||
.andWhere("NOT (:userId = ANY(reads))", { userId: user.id })
|
|
||||||
.execute(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
publishMainStream(user.id, "readAllMessagingMessages");
|
|
||||||
});
|
|
|
@ -1,112 +0,0 @@
|
||||||
import { Brackets } from "typeorm";
|
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import {
|
|
||||||
MessagingMessages,
|
|
||||||
Mutings,
|
|
||||||
UserGroupJoinings,
|
|
||||||
} from "@/models/index.js";
|
|
||||||
import define from "../../define.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["messaging"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "read:messaging",
|
|
||||||
|
|
||||||
res: {
|
|
||||||
type: "array",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
items: {
|
|
||||||
type: "object",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
ref: "MessagingMessage",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
limit: { type: "integer", minimum: 1, maximum: 100, default: 10 },
|
|
||||||
group: { type: "boolean", default: false },
|
|
||||||
},
|
|
||||||
required: [],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
const mute = await Mutings.findBy({
|
|
||||||
muterId: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const groups = ps.group
|
|
||||||
? await UserGroupJoinings.findBy({
|
|
||||||
userId: user.id,
|
|
||||||
}).then((xs) => xs.map((x) => x.userGroupId))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
if (ps.group && groups.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const history: MessagingMessage[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < ps.limit; i++) {
|
|
||||||
const found = ps.group
|
|
||||||
? history.map((m) => m.groupId!)
|
|
||||||
: history.map((m) => (m.userId === user.id ? m.recipientId! : m.userId!));
|
|
||||||
|
|
||||||
const query = MessagingMessages.createQueryBuilder("message").orderBy(
|
|
||||||
"message.createdAt",
|
|
||||||
"DESC",
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ps.group) {
|
|
||||||
query.where("message.groupId IN (:...groups)", { groups: groups });
|
|
||||||
|
|
||||||
if (found.length > 0) {
|
|
||||||
query.andWhere("message.groupId NOT IN (:...found)", { found: found });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
query.where(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where("message.userId = :userId", { userId: user.id }).orWhere(
|
|
||||||
"message.recipientId = :userId",
|
|
||||||
{ userId: user.id },
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
query.andWhere("message.groupId IS NULL");
|
|
||||||
|
|
||||||
if (found.length > 0) {
|
|
||||||
query.andWhere("message.userId NOT IN (:...found)", { found: found });
|
|
||||||
query.andWhere("message.recipientId NOT IN (:...found)", {
|
|
||||||
found: found,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mute.length > 0) {
|
|
||||||
query.andWhere("message.userId NOT IN (:...mute)", {
|
|
||||||
mute: mute.map((m) => m.muteeId),
|
|
||||||
});
|
|
||||||
query.andWhere("message.recipientId NOT IN (:...mute)", {
|
|
||||||
mute: mute.map((m) => m.muteeId),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = await query.getOne();
|
|
||||||
|
|
||||||
if (message) {
|
|
||||||
history.push(message);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
history.map((h) => MessagingMessages.pack(h.id, user)),
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -1,182 +0,0 @@
|
||||||
import define from "../../define.js";
|
|
||||||
import { ApiError } from "../../error.js";
|
|
||||||
import { getUser } from "../../common/getters.js";
|
|
||||||
import {
|
|
||||||
MessagingMessages,
|
|
||||||
UserGroups,
|
|
||||||
UserGroupJoinings,
|
|
||||||
Users,
|
|
||||||
} from "@/models/index.js";
|
|
||||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
|
||||||
import { Brackets } from "typeorm";
|
|
||||||
import {
|
|
||||||
readUserMessagingMessage,
|
|
||||||
readGroupMessagingMessage,
|
|
||||||
deliverReadActivity,
|
|
||||||
} from "../../common/read-messaging-message.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["messaging"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "read:messaging",
|
|
||||||
|
|
||||||
res: {
|
|
||||||
type: "array",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
items: {
|
|
||||||
type: "object",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
ref: "MessagingMessage",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
errors: {
|
|
||||||
noSuchUser: {
|
|
||||||
message: "No such user.",
|
|
||||||
code: "NO_SUCH_USER",
|
|
||||||
id: "11795c64-40ea-4198-b06e-3c873ed9039d",
|
|
||||||
},
|
|
||||||
|
|
||||||
noSuchGroup: {
|
|
||||||
message: "No such group.",
|
|
||||||
code: "NO_SUCH_GROUP",
|
|
||||||
id: "c4d9f88c-9270-4632-b032-6ed8cee36f7f",
|
|
||||||
},
|
|
||||||
|
|
||||||
groupAccessDenied: {
|
|
||||||
message: "You can not read messages of groups that you have not joined.",
|
|
||||||
code: "GROUP_ACCESS_DENIED",
|
|
||||||
id: "a053a8dd-a491-4718-8f87-50775aad9284",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
limit: { type: "integer", minimum: 1, maximum: 100, default: 10 },
|
|
||||||
sinceId: { type: "string", format: "misskey:id" },
|
|
||||||
untilId: { type: "string", format: "misskey:id" },
|
|
||||||
markAsRead: { type: "boolean", default: true },
|
|
||||||
},
|
|
||||||
anyOf: [
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
userId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
required: ["userId"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
groupId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
required: ["groupId"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
if (ps.userId != null) {
|
|
||||||
// Fetch recipient (user)
|
|
||||||
const recipient = await getUser(ps.userId).catch((e) => {
|
|
||||||
if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff")
|
|
||||||
throw new ApiError(meta.errors.noSuchUser);
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
|
|
||||||
const query = makePaginationQuery(
|
|
||||||
MessagingMessages.createQueryBuilder("message"),
|
|
||||||
ps.sinceId,
|
|
||||||
ps.untilId,
|
|
||||||
)
|
|
||||||
.andWhere(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where("message.userId = :meId").andWhere(
|
|
||||||
"message.recipientId = :recipientId",
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
).orWhere(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where("message.userId = :recipientId").andWhere(
|
|
||||||
"message.recipientId = :meId",
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.setParameter("meId", user.id)
|
|
||||||
.setParameter("recipientId", recipient.id);
|
|
||||||
|
|
||||||
const messages = await query.take(ps.limit).getMany();
|
|
||||||
|
|
||||||
// Mark all as read
|
|
||||||
if (ps.markAsRead) {
|
|
||||||
readUserMessagingMessage(
|
|
||||||
user.id,
|
|
||||||
recipient.id,
|
|
||||||
messages.filter((m) => m.recipientId === user.id).map((x) => x.id),
|
|
||||||
);
|
|
||||||
|
|
||||||
// リモートユーザーとのメッセージだったら既読配信
|
|
||||||
if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) {
|
|
||||||
deliverReadActivity(user, recipient, messages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
messages.map((message) =>
|
|
||||||
MessagingMessages.pack(message, user, {
|
|
||||||
populateRecipient: false,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (ps.groupId != null) {
|
|
||||||
// Fetch recipient (group)
|
|
||||||
const recipientGroup = await UserGroups.findOneBy({ id: ps.groupId });
|
|
||||||
|
|
||||||
if (recipientGroup == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check joined
|
|
||||||
const joining = await UserGroupJoinings.findOneBy({
|
|
||||||
userId: user.id,
|
|
||||||
userGroupId: recipientGroup.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
throw new ApiError(meta.errors.groupAccessDenied);
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = makePaginationQuery(
|
|
||||||
MessagingMessages.createQueryBuilder("message"),
|
|
||||||
ps.sinceId,
|
|
||||||
ps.untilId,
|
|
||||||
).andWhere("message.groupId = :groupId", { groupId: recipientGroup.id });
|
|
||||||
|
|
||||||
const messages = await query.take(ps.limit).getMany();
|
|
||||||
|
|
||||||
// Mark all as read
|
|
||||||
if (ps.markAsRead) {
|
|
||||||
readGroupMessagingMessage(
|
|
||||||
user.id,
|
|
||||||
recipientGroup.id,
|
|
||||||
messages.map((x) => x.id),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
messages.map((message) =>
|
|
||||||
MessagingMessages.pack(message, user, {
|
|
||||||
populateGroup: false,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,165 +0,0 @@
|
||||||
import define from "../../../define.js";
|
|
||||||
import { ApiError } from "../../../error.js";
|
|
||||||
import { getUser } from "../../../common/getters.js";
|
|
||||||
import {
|
|
||||||
MessagingMessages,
|
|
||||||
DriveFiles,
|
|
||||||
UserGroups,
|
|
||||||
UserGroupJoinings,
|
|
||||||
Blockings,
|
|
||||||
} from "@/models/index.js";
|
|
||||||
import type { User } from "@/models/entities/user.js";
|
|
||||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
|
||||||
import { createMessage } from "@/services/messages/create.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["messaging"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "write:messaging",
|
|
||||||
|
|
||||||
res: {
|
|
||||||
type: "object",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
ref: "MessagingMessage",
|
|
||||||
},
|
|
||||||
|
|
||||||
errors: {
|
|
||||||
recipientIsYourself: {
|
|
||||||
message: "You can not send a message to yourself.",
|
|
||||||
code: "RECIPIENT_IS_YOURSELF",
|
|
||||||
id: "17e2ba79-e22a-4cbc-bf91-d327643f4a7e",
|
|
||||||
},
|
|
||||||
|
|
||||||
noSuchUser: {
|
|
||||||
message: "No such user.",
|
|
||||||
code: "NO_SUCH_USER",
|
|
||||||
id: "11795c64-40ea-4198-b06e-3c873ed9039d",
|
|
||||||
},
|
|
||||||
|
|
||||||
noSuchGroup: {
|
|
||||||
message: "No such group.",
|
|
||||||
code: "NO_SUCH_GROUP",
|
|
||||||
id: "c94e2a5d-06aa-4914-8fa6-6a42e73d6537",
|
|
||||||
},
|
|
||||||
|
|
||||||
groupAccessDenied: {
|
|
||||||
message: "You can not send messages to groups that you have not joined.",
|
|
||||||
code: "GROUP_ACCESS_DENIED",
|
|
||||||
id: "d96b3cca-5ad1-438b-ad8b-02f931308fbd",
|
|
||||||
},
|
|
||||||
|
|
||||||
noSuchFile: {
|
|
||||||
message: "No such file.",
|
|
||||||
code: "NO_SUCH_FILE",
|
|
||||||
id: "4372b8e2-185d-4146-8749-2f68864a3e5f",
|
|
||||||
},
|
|
||||||
|
|
||||||
contentRequired: {
|
|
||||||
message: "Content required. You need to set text or fileId.",
|
|
||||||
code: "CONTENT_REQUIRED",
|
|
||||||
id: "25587321-b0e6-449c-9239-f8925092942c",
|
|
||||||
},
|
|
||||||
|
|
||||||
youHaveBeenBlocked: {
|
|
||||||
message:
|
|
||||||
"You cannot send a message because you have been blocked by this user.",
|
|
||||||
code: "YOU_HAVE_BEEN_BLOCKED",
|
|
||||||
id: "c15a5199-7422-4968-941a-2a462c478f7d",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
text: { type: "string", nullable: true, maxLength: 3000 },
|
|
||||||
fileId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
anyOf: [
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
userId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
required: ["userId"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
groupId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
required: ["groupId"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
let recipientUser: User | null;
|
|
||||||
let recipientGroup: UserGroup | null;
|
|
||||||
|
|
||||||
if (ps.userId != null) {
|
|
||||||
// Myself
|
|
||||||
if (ps.userId === user.id) {
|
|
||||||
throw new ApiError(meta.errors.recipientIsYourself);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch recipient (user)
|
|
||||||
recipientUser = await getUser(ps.userId).catch((e) => {
|
|
||||||
if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff")
|
|
||||||
throw new ApiError(meta.errors.noSuchUser);
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check blocking
|
|
||||||
const block = await Blockings.findOneBy({
|
|
||||||
blockerId: recipientUser.id,
|
|
||||||
blockeeId: user.id,
|
|
||||||
});
|
|
||||||
if (block) {
|
|
||||||
throw new ApiError(meta.errors.youHaveBeenBlocked);
|
|
||||||
}
|
|
||||||
} else if (ps.groupId != null) {
|
|
||||||
// Fetch recipient (group)
|
|
||||||
recipientGroup = await UserGroups.findOneBy({ id: ps.groupId! });
|
|
||||||
|
|
||||||
if (recipientGroup == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check joined
|
|
||||||
const joining = await UserGroupJoinings.findOneBy({
|
|
||||||
userId: user.id,
|
|
||||||
userGroupId: recipientGroup.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
throw new ApiError(meta.errors.groupAccessDenied);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = null;
|
|
||||||
if (ps.fileId != null) {
|
|
||||||
file = await DriveFiles.findOneBy({
|
|
||||||
id: ps.fileId,
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (file == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// テキストが無いかつ添付ファイルも無かったらエラー
|
|
||||||
if (ps.text == null && file == null) {
|
|
||||||
throw new ApiError(meta.errors.contentRequired);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await createMessage(
|
|
||||||
user,
|
|
||||||
recipientUser,
|
|
||||||
recipientGroup,
|
|
||||||
ps.text,
|
|
||||||
file,
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -1,48 +0,0 @@
|
||||||
import define from "../../../define.js";
|
|
||||||
import { ApiError } from "../../../error.js";
|
|
||||||
import { MessagingMessages } from "@/models/index.js";
|
|
||||||
import { deleteMessage } from "@/services/messages/delete.js";
|
|
||||||
import { SECOND, HOUR } from "@/const.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["messaging"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "write:messaging",
|
|
||||||
|
|
||||||
limit: {
|
|
||||||
duration: HOUR,
|
|
||||||
max: 300,
|
|
||||||
minInterval: SECOND,
|
|
||||||
},
|
|
||||||
|
|
||||||
errors: {
|
|
||||||
noSuchMessage: {
|
|
||||||
message: "No such message.",
|
|
||||||
code: "NO_SUCH_MESSAGE",
|
|
||||||
id: "54b5b326-7925-42cf-8019-130fda8b56af",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
messageId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
required: ["messageId"],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
const message = await MessagingMessages.findOneBy({
|
|
||||||
id: ps.messageId,
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (message == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteMessage(message);
|
|
||||||
});
|
|
|
@ -1,57 +0,0 @@
|
||||||
import define from "../../../define.js";
|
|
||||||
import { ApiError } from "../../../error.js";
|
|
||||||
import { MessagingMessages } from "@/models/index.js";
|
|
||||||
import {
|
|
||||||
readUserMessagingMessage,
|
|
||||||
readGroupMessagingMessage,
|
|
||||||
} from "../../../common/read-messaging-message.js";
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ["messaging"],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: "write:messaging",
|
|
||||||
|
|
||||||
errors: {
|
|
||||||
noSuchMessage: {
|
|
||||||
message: "No such message.",
|
|
||||||
code: "NO_SUCH_MESSAGE",
|
|
||||||
id: "86d56a2f-a9c3-4afb-b13c-3e9bfef9aa14",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
messageId: { type: "string", format: "misskey:id" },
|
|
||||||
},
|
|
||||||
required: ["messageId"],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
const message = await MessagingMessages.findOneBy({ id: ps.messageId });
|
|
||||||
|
|
||||||
if (message == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.recipientId) {
|
|
||||||
await readUserMessagingMessage(user.id, message.userId, [message.id]).catch(
|
|
||||||
(e) => {
|
|
||||||
if (e.id === "e140a4bf-49ce-4fb6-b67c-b78dadf6b52f")
|
|
||||||
throw new ApiError(meta.errors.noSuchMessage);
|
|
||||||
throw e;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else if (message.groupId) {
|
|
||||||
await readGroupMessagingMessage(user.id, message.groupId, [
|
|
||||||
message.id,
|
|
||||||
]).catch((e) => {
|
|
||||||
if (e.id === "930a270c-714a-46b2-b776-ad27276dc569")
|
|
||||||
throw new ApiError(meta.errors.noSuchMessage);
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -7,7 +7,6 @@ import Router from "@koa/router";
|
||||||
import multer from "@koa/multer";
|
import multer from "@koa/multer";
|
||||||
import bodyParser from "koa-bodyparser";
|
import bodyParser from "koa-bodyparser";
|
||||||
import cors from "@koa/cors";
|
import cors from "@koa/cors";
|
||||||
import { apiMastodonCompatible } from './mastodon/ApiMastodonCompatibleService.js';
|
|
||||||
import { Instances, AccessTokens, Users } from "@/models/index.js";
|
import { Instances, AccessTokens, Users } from "@/models/index.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import endpoints from "./endpoints.js";
|
import endpoints from "./endpoints.js";
|
||||||
|
@ -58,8 +57,6 @@ const upload = multer({
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
apiMastodonCompatible(router);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register endpoint handlers
|
* Register endpoint handlers
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
import Router from "@koa/router";
|
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import { apiAuthMastodon } from './endpoints/auth.js';
|
|
||||||
import { apiAccountMastodon } from './endpoints/account.js';
|
|
||||||
import { apiStatusMastodon } from './endpoints/status.js';
|
|
||||||
import { apiFilterMastodon } from './endpoints/filter.js';
|
|
||||||
import { apiTimelineMastodon } from './endpoints/timeline.js';
|
|
||||||
import { apiNotificationsMastodon } from './endpoints/notifications.js';
|
|
||||||
import { apiSearchMastodon } from './endpoints/search.js';
|
|
||||||
import { getInstance } from './endpoints/meta.js';
|
|
||||||
|
|
||||||
export function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface {
|
|
||||||
const accessTokenArr = authorization?.split(' ') ?? [null];
|
|
||||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
|
||||||
const generator = (megalodon as any).default
|
|
||||||
const client = generator('misskey', BASE_URL, accessToken) as MegalodonInterface;
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
export function apiMastodonCompatible(router: Router): void {
|
|
||||||
apiAuthMastodon(router)
|
|
||||||
apiAccountMastodon(router)
|
|
||||||
apiStatusMastodon(router)
|
|
||||||
apiFilterMastodon(router)
|
|
||||||
apiTimelineMastodon(router)
|
|
||||||
apiNotificationsMastodon(router)
|
|
||||||
apiSearchMastodon(router)
|
|
||||||
|
|
||||||
router.get('/v1/custom_emojis', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getInstanceCustomEmojis();
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/v1/instance', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
|
|
||||||
// displayed without being logged in
|
|
||||||
try {
|
|
||||||
const data = await client.getInstance();
|
|
||||||
ctx.body = getInstance(data.data);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,323 +0,0 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
import { toLimitToInt } from './timeline.js';
|
|
||||||
|
|
||||||
export function apiAccountMastodon(router: Router): void {
|
|
||||||
|
|
||||||
router.get('/v1/accounts/verify_credentials', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.verifyAccountCredentials();
|
|
||||||
const acct = data.data;
|
|
||||||
acct.url = `${BASE_URL}/@${acct.url}`
|
|
||||||
acct.note = ''
|
|
||||||
acct.avatar_static = acct.avatar
|
|
||||||
acct.header = acct.header || ''
|
|
||||||
acct.header_static = acct.header || ''
|
|
||||||
acct.source = {
|
|
||||||
note: acct.note,
|
|
||||||
fields: acct.fields,
|
|
||||||
privacy: 'public',
|
|
||||||
sensitive: false,
|
|
||||||
language: ''
|
|
||||||
}
|
|
||||||
ctx.body = acct
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.patch('/v1/accounts/update_credentials', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.updateCredentials((ctx.request as any).body as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getAccount(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/statuses', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getAccountStatuses(ctx.params.id, toLimitToInt(ctx.query as any));
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/followers', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getAccountFollowers(ctx.params.id, ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/following', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getAccountFollowing(ctx.params.id, ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/lists', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getAccountLists(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/follow', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.followAccount(ctx.params.id);
|
|
||||||
const acct = data.data;
|
|
||||||
acct.following = true;
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/unfollow', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.unfollowAccount(ctx.params.id);
|
|
||||||
const acct = data.data;
|
|
||||||
acct.following = false;
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/block', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.blockAccount(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/unblock', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.unblockAccount(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/mute', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.muteAccount(ctx.params.id, (ctx.request as any).body as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/unmute', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.unmuteAccount(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/accounts/relationships', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const idsRaw = (ctx.query as any)['id[]']
|
|
||||||
const ids = typeof idsRaw === 'string' ? [idsRaw] : idsRaw
|
|
||||||
const data = await client.getRelationships(ids) as any;
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/bookmarks', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getBookmarks(ctx.query as any) as any;
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/favourites', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getFavourites(ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/mutes', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getMutes(ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/blocks', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getBlocks(ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/follow_ctxs', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getFollowRequests((ctx.query as any || { limit: 20 }).limit);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/follow_ctxs/:id/authorize', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.acceptFollowRequest(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/follow_ctxs/:id/reject', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.rejectFollowRequest(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status =(401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
|
|
||||||
const readScope = [
|
|
||||||
'read:account',
|
|
||||||
'read:drive',
|
|
||||||
'read:blocks',
|
|
||||||
'read:favorites',
|
|
||||||
'read:following',
|
|
||||||
'read:messaging',
|
|
||||||
'read:mutes',
|
|
||||||
'read:notifications',
|
|
||||||
'read:reactions',
|
|
||||||
'read:pages',
|
|
||||||
'read:page-likes',
|
|
||||||
'read:user-groups',
|
|
||||||
'read:channels',
|
|
||||||
'read:gallery',
|
|
||||||
'read:gallery-likes'
|
|
||||||
]
|
|
||||||
const writeScope = [
|
|
||||||
'write:account',
|
|
||||||
'write:drive',
|
|
||||||
'write:blocks',
|
|
||||||
'write:favorites',
|
|
||||||
'write:following',
|
|
||||||
'write:messaging',
|
|
||||||
'write:mutes',
|
|
||||||
'write:notes',
|
|
||||||
'write:notifications',
|
|
||||||
'write:reactions',
|
|
||||||
'write:votes',
|
|
||||||
'write:pages',
|
|
||||||
'write:page-likes',
|
|
||||||
'write:user-groups',
|
|
||||||
'write:channels',
|
|
||||||
'write:gallery',
|
|
||||||
'write:gallery-likes'
|
|
||||||
]
|
|
||||||
|
|
||||||
export function apiAuthMastodon(router: Router): void {
|
|
||||||
|
|
||||||
router.post('/v1/apps', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
let scope = body.scopes
|
|
||||||
console.log(body)
|
|
||||||
if (typeof scope === 'string') scope = scope.split(' ')
|
|
||||||
const pushScope = new Set<string>()
|
|
||||||
for (const s of scope) {
|
|
||||||
if (s.match(/^read/)) for (const r of readScope) pushScope.add(r)
|
|
||||||
if (s.match(/^write/)) for (const r of writeScope) pushScope.add(r)
|
|
||||||
}
|
|
||||||
const scopeArr = Array.from(pushScope)
|
|
||||||
|
|
||||||
let red = body.redirect_uris
|
|
||||||
if (red === 'urn:ietf:wg:oauth:2.0:oob') {
|
|
||||||
red = 'https://thedesk.top/hello.html'
|
|
||||||
}
|
|
||||||
const appData = await client.registerApp(body.client_name, { scopes: scopeArr, redirect_uris: red, website: body.website });
|
|
||||||
ctx.body = {
|
|
||||||
id: appData.id,
|
|
||||||
name: appData.name,
|
|
||||||
website: appData.website,
|
|
||||||
redirect_uri: red,
|
|
||||||
client_id: Buffer.from(appData.url || '').toString('base64'),
|
|
||||||
client_secret: appData.clientSecret,
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
|
|
||||||
export function apiFilterMastodon(router: Router): void {
|
|
||||||
|
|
||||||
router.get('/v1/filters', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.getFilters();
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/v1/filters/:id', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.getFilter(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/v1/filters', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.createFilter(body.phrase, body.context, body);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/v1/filters/:id', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.updateFilter(ctx.params.id, body.phrase, body.context);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.delete('/v1/filters/:id', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.deleteFilter(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
import { Entity } from "@cutls/megalodon";
|
|
||||||
// TODO: add calckey features
|
|
||||||
export function getInstance(response: Entity.Instance) {
|
|
||||||
return {
|
|
||||||
uri: response.uri,
|
|
||||||
title: response.title || "",
|
|
||||||
short_description: response.description || "",
|
|
||||||
description: response.description || "",
|
|
||||||
email: response.email || "",
|
|
||||||
version: "3.0.0 compatible (Calckey)",
|
|
||||||
urls: response.urls,
|
|
||||||
stats: response.stats,
|
|
||||||
thumbnail: response.thumbnail || "",
|
|
||||||
languages: ["en", "de", "ja"],
|
|
||||||
registrations: response.registrations,
|
|
||||||
approval_required: !response.registrations,
|
|
||||||
invites_enabled: response.registrations,
|
|
||||||
configuration: {
|
|
||||||
accounts: {
|
|
||||||
max_featured_tags: 20,
|
|
||||||
},
|
|
||||||
statuses: {
|
|
||||||
max_characters: 3000,
|
|
||||||
max_media_attachments: 4,
|
|
||||||
characters_reserved_per_url: response.uri.length,
|
|
||||||
},
|
|
||||||
media_attachments: {
|
|
||||||
supported_mime_types: [
|
|
||||||
"image/jpeg",
|
|
||||||
"image/png",
|
|
||||||
"image/gif",
|
|
||||||
"image/heic",
|
|
||||||
"image/heif",
|
|
||||||
"image/webp",
|
|
||||||
"image/avif",
|
|
||||||
"video/webm",
|
|
||||||
"video/mp4",
|
|
||||||
"video/quicktime",
|
|
||||||
"video/ogg",
|
|
||||||
"audio/wave",
|
|
||||||
"audio/wav",
|
|
||||||
"audio/x-wav",
|
|
||||||
"audio/x-pn-wave",
|
|
||||||
"audio/vnd.wave",
|
|
||||||
"audio/ogg",
|
|
||||||
"audio/vorbis",
|
|
||||||
"audio/mpeg",
|
|
||||||
"audio/mp3",
|
|
||||||
"audio/webm",
|
|
||||||
"audio/flac",
|
|
||||||
"audio/aac",
|
|
||||||
"audio/m4a",
|
|
||||||
"audio/x-m4a",
|
|
||||||
"audio/mp4",
|
|
||||||
"audio/3gpp",
|
|
||||||
"video/x-ms-asf",
|
|
||||||
],
|
|
||||||
image_size_limit: 10485760,
|
|
||||||
image_matrix_limit: 16777216,
|
|
||||||
video_size_limit: 41943040,
|
|
||||||
video_frame_rate_limit: 60,
|
|
||||||
video_matrix_limit: 2304000,
|
|
||||||
},
|
|
||||||
polls: {
|
|
||||||
max_options: 8,
|
|
||||||
max_characters_per_option: 50,
|
|
||||||
min_expiration: 300,
|
|
||||||
max_expiration: 2629746,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
contact_account: {
|
|
||||||
id: "1",
|
|
||||||
username: "admin",
|
|
||||||
acct: "admin",
|
|
||||||
display_name: "admin",
|
|
||||||
locked: true,
|
|
||||||
bot: true,
|
|
||||||
discoverable: false,
|
|
||||||
group: false,
|
|
||||||
created_at: "1971-01-01T00:00:00.000Z",
|
|
||||||
note: "",
|
|
||||||
url: "https://http.cat/404",
|
|
||||||
avatar: "https://http.cat/404",
|
|
||||||
avatar_static: "https://http.cat/404",
|
|
||||||
header: "https://http.cat/404",
|
|
||||||
header_static: "https://http.cat/404",
|
|
||||||
followers_count: -1,
|
|
||||||
following_count: 0,
|
|
||||||
statuses_count: 0,
|
|
||||||
last_status_at: "1971-01-01T00:00:00.000Z",
|
|
||||||
noindex: true,
|
|
||||||
emojis: [],
|
|
||||||
fields: [],
|
|
||||||
},
|
|
||||||
rules: [],
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
import { toTextWithReaction } from './timeline.js';
|
|
||||||
function toLimitToInt(q: any) {
|
|
||||||
if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10)
|
|
||||||
return q
|
|
||||||
}
|
|
||||||
|
|
||||||
export function apiNotificationsMastodon(router: Router): void {
|
|
||||||
|
|
||||||
router.get('/v1/notifications', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.getNotifications(toLimitToInt(ctx.query));
|
|
||||||
const notfs = data.data;
|
|
||||||
const ret = notfs.map((n) => {
|
|
||||||
if(n.type !== 'follow' && n.type !== 'follow_request') {
|
|
||||||
if (n.type === 'reaction') n.type = 'favourite'
|
|
||||||
n.status = toTextWithReaction(n.status ? [n.status] : [], ctx.hostname)[0]
|
|
||||||
return n
|
|
||||||
} else {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
})
|
|
||||||
ctx.body = ret;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/v1/notification/:id', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const dataRaw = await client.getNotification(ctx.params.id);
|
|
||||||
const data = dataRaw.data;
|
|
||||||
if(data.type !== 'follow' && data.type !== 'follow_request') {
|
|
||||||
if (data.type === 'reaction') data.type = 'favourite'
|
|
||||||
ctx.body = toTextWithReaction([data as any], ctx.request.hostname)[0]
|
|
||||||
} else {
|
|
||||||
ctx.body = data
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/v1/notifications/clear', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.dismissNotifications();
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/v1/notification/:id/dismiss', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const data = await client.dismissNotification(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
|
|
||||||
export function apiSearchMastodon(router: Router): void {
|
|
||||||
|
|
||||||
router.get('/v1/search', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
try {
|
|
||||||
const query: any = ctx.query
|
|
||||||
const type = query.type || ''
|
|
||||||
const data = await client.search(query.q, type, query);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,403 +0,0 @@
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
import fs from 'fs'
|
|
||||||
import { pipeline } from 'node:stream';
|
|
||||||
import { promisify } from 'node:util';
|
|
||||||
import { createTemp } from '@/misc/create-temp.js';
|
|
||||||
import { emojiRegex, emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js';
|
|
||||||
import axios from 'axios';
|
|
||||||
const pump = promisify(pipeline);
|
|
||||||
|
|
||||||
export function apiStatusMastodon(router: Router): void {
|
|
||||||
router.post('/v1/statuses', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const body: any = ctx.request.body
|
|
||||||
const text = body.status
|
|
||||||
const removed = text.replace(/@\S+/g, '').replaceAll(' ', '')
|
|
||||||
const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed)
|
|
||||||
const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed)
|
|
||||||
if (body.in_reply_to_id && isDefaultEmoji || isCustomEmoji) {
|
|
||||||
const a = await client.createEmojiReaction(body.in_reply_to_id, removed)
|
|
||||||
ctx.body = a.data
|
|
||||||
}
|
|
||||||
if (body.in_reply_to_id && removed === '/unreact') {
|
|
||||||
try {
|
|
||||||
const id = body.in_reply_to_id
|
|
||||||
const post = await client.getStatus(id)
|
|
||||||
const react = post.data.emoji_reactions.filter((e) => e.me)[0].name
|
|
||||||
const data = await client.deleteEmojiReaction(id, react);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!body.media_ids) body.media_ids = undefined
|
|
||||||
if (body.media_ids && !body.media_ids.length) body.media_ids = undefined
|
|
||||||
const data = await client.postStatus(text, body);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.delete<{ Params: { id: string } }>('/v1/statuses/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.deleteStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
interface IReaction {
|
|
||||||
id: string
|
|
||||||
createdAt: string
|
|
||||||
user: MisskeyEntity.User,
|
|
||||||
type: string
|
|
||||||
}
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id/context', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const id = ctx.params.id
|
|
||||||
const data = await client.getStatusContext(id, ctx.query as any);
|
|
||||||
const status = await client.getStatus(id);
|
|
||||||
const reactionsAxios = await axios.get(`${BASE_URL}/api/notes/reactions?noteId=${id}`)
|
|
||||||
const reactions: IReaction[] = reactionsAxios.data
|
|
||||||
const text = reactions.map((r) => `${r.type.replace('@.', '')} ${r.user.username}`).join('<br />')
|
|
||||||
data.data.descendants.unshift(statusModel(status.data.id, status.data.account.id, status.data.emojis, text))
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id/reblogged_by', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getStatusRebloggedBy(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id/favourited_by', async (ctx, reply) => {
|
|
||||||
ctx.body = []
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/favourite', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const react = await getFirstReaction(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const a = await client.createEmojiReaction(ctx.params.id, react) as any;
|
|
||||||
//const data = await client.favouriteStatus(ctx.params.id) as any;
|
|
||||||
ctx.body = a.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unfavourite', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
const react = await getFirstReaction(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.deleteEmojiReaction(ctx.params.id, react);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/reblog', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.reblogStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unreblog', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.unreblogStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/bookmark', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.bookmarkStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unbookmark', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.unbookmarkStatus(ctx.params.id) as any;
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/pin', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.pinStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unpin', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.unpinStatus(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post('/v1/media', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const multipartData = await ctx.file;
|
|
||||||
if (!multipartData) {
|
|
||||||
ctx.body = { error: 'No image' };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const [path] = await createTemp();
|
|
||||||
await pump(multipartData.buffer, fs.createWriteStream(path));
|
|
||||||
const image = fs.readFileSync(path);
|
|
||||||
const data = await client.uploadMedia(image);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post('/v2/media', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const multipartData = await ctx.file;
|
|
||||||
if (!multipartData) {
|
|
||||||
ctx.body = { error: 'No image' };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const [path] = await createTemp();
|
|
||||||
await pump(multipartData.buffer, fs.createWriteStream(path));
|
|
||||||
const image = fs.readFileSync(path);
|
|
||||||
const data = await client.uploadMedia(image);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/media/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getMedia(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.put<{ Params: { id: string } }>('/v1/media/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.updateMedia(ctx.params.id, ctx.request.body as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/polls/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getPoll(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/polls/:id/votes', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.votePoll(ctx.params.id, (ctx.request.body as any).choices);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getFirstReaction(BASE_URL: string, accessTokens: string | undefined) {
|
|
||||||
const accessTokenArr = accessTokens?.split(' ') ?? [null];
|
|
||||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
|
||||||
let react = '👍'
|
|
||||||
try {
|
|
||||||
const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, {
|
|
||||||
scope: ['client', 'base'],
|
|
||||||
key: 'reactions',
|
|
||||||
i: accessToken
|
|
||||||
})
|
|
||||||
const reactRaw = api.data
|
|
||||||
react = Array.isArray(reactRaw) ? api.data[0] : '👍'
|
|
||||||
console.log(api.data)
|
|
||||||
return react
|
|
||||||
} catch (e) {
|
|
||||||
return react
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function statusModel(id: string | null, acctId: string | null, emojis: MastodonEntity.Emoji[], content: string) {
|
|
||||||
const now = "1970-01-02T00:00:00.000Z"
|
|
||||||
return {
|
|
||||||
id: '9atm5frjhb',
|
|
||||||
uri: 'https://http.cat/404', // ""
|
|
||||||
url: 'https://http.cat/404', // "",
|
|
||||||
account: {
|
|
||||||
id: '9arzuvv0sw',
|
|
||||||
username: 'ReactionBot',
|
|
||||||
acct: 'ReactionBot',
|
|
||||||
display_name: 'ReactionOfThisPost',
|
|
||||||
locked: false,
|
|
||||||
created_at: now,
|
|
||||||
followers_count: 0,
|
|
||||||
following_count: 0,
|
|
||||||
statuses_count: 0,
|
|
||||||
note: '',
|
|
||||||
url: 'https://http.cat/404',
|
|
||||||
avatar: 'https://http.cat/404',
|
|
||||||
avatar_static: 'https://http.cat/404',
|
|
||||||
header: 'https://http.cat/404', // ""
|
|
||||||
header_static: 'https://http.cat/404', // ""
|
|
||||||
emojis: [],
|
|
||||||
fields: [],
|
|
||||||
moved: null,
|
|
||||||
bot: false,
|
|
||||||
},
|
|
||||||
in_reply_to_id: id,
|
|
||||||
in_reply_to_account_id: acctId,
|
|
||||||
reblog: null,
|
|
||||||
content: `<p>${content}</p>`,
|
|
||||||
plain_content: null,
|
|
||||||
created_at: now,
|
|
||||||
emojis: emojis,
|
|
||||||
replies_count: 0,
|
|
||||||
reblogs_count: 0,
|
|
||||||
favourites_count: 0,
|
|
||||||
favourited: false,
|
|
||||||
reblogged: false,
|
|
||||||
muted: false,
|
|
||||||
sensitive: false,
|
|
||||||
spoiler_text: '',
|
|
||||||
visibility: 'public' as const,
|
|
||||||
media_attachments: [],
|
|
||||||
mentions: [],
|
|
||||||
tags: [],
|
|
||||||
card: null,
|
|
||||||
poll: null,
|
|
||||||
application: null,
|
|
||||||
language: null,
|
|
||||||
pinned: false,
|
|
||||||
emoji_reactions: [],
|
|
||||||
bookmarked: false,
|
|
||||||
quote: false,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,246 +0,0 @@
|
||||||
import Router from "@koa/router";
|
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import megalodon, { Entity, MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js'
|
|
||||||
import { statusModel } from './status.js';
|
|
||||||
import Autolinker from 'autolinker';
|
|
||||||
import { ParsedUrlQuery } from "querystring";
|
|
||||||
|
|
||||||
export function toLimitToInt(q: ParsedUrlQuery) {
|
|
||||||
if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10).toString()
|
|
||||||
return q
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toTextWithReaction(status: Entity.Status[], host: string) {
|
|
||||||
return status.map((t) => {
|
|
||||||
if (!t) return statusModel(null, null, [], 'no content')
|
|
||||||
if (!t.emoji_reactions) return t
|
|
||||||
if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0]
|
|
||||||
const reactions = t.emoji_reactions.map((r) => `${r.name.replace('@.', '')} (${r.count}${r.me ? "* " : ''})`);
|
|
||||||
//t.emojis = getEmoji(t.content, host)
|
|
||||||
t.content = `<p>${autoLinker(t.content, host)}</p><p>${reactions.join(', ')}</p>`
|
|
||||||
return t
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export function autoLinker(input: string, host: string) {
|
|
||||||
return Autolinker.link(input, {
|
|
||||||
hashtag: 'twitter',
|
|
||||||
mention: 'twitter',
|
|
||||||
email: false,
|
|
||||||
stripPrefix: false,
|
|
||||||
replaceFn : function (match) {
|
|
||||||
switch(match.type) {
|
|
||||||
case 'url':
|
|
||||||
return true
|
|
||||||
case 'mention':
|
|
||||||
console.log("Mention: ", match.getMention());
|
|
||||||
console.log("Mention Service Name: ", match.getServiceName());
|
|
||||||
return `<a href="https://${host}/@${encodeURIComponent(match.getMention())}" target="_blank">@${match.getMention()}</a>`;
|
|
||||||
case 'hashtag':
|
|
||||||
console.log("Hashtag: ", match.getHashtag());
|
|
||||||
return `<a href="https://${host}/tags/${encodeURIComponent(match.getHashtag())}" target="_blank">#${match.getHashtag()}</a>`;
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
export function apiTimelineMastodon(router: Router): void {
|
|
||||||
router.get('/v1/timelines/public', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const query: any = ctx.query
|
|
||||||
const data = query.local ? await client.getLocalTimeline(toLimitToInt(query)) : await client.getPublicTimeline(toLimitToInt(query));
|
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { hashtag: string } }>('/v1/timelines/tag/:hashtag', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getTagTimeline(ctx.params.hashtag, toLimitToInt(ctx.query));
|
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { hashtag: string } }>('/v1/timelines/home', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getHomeTimeline(toLimitToInt(ctx.query));
|
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { listId: string } }>('/v1/timelines/list/:listId', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getListTimeline(ctx.params.listId, toLimitToInt(ctx.query));
|
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/conversations', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getConversationTimeline(toLimitToInt(ctx.query));
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get('/v1/lists', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getLists();
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getList(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post('/v1/lists', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.createList((ctx.query as any).title);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.put<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.updateList(ctx.params.id, ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.delete<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.deleteList(ctx.params.id);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.getAccountsInList(ctx.params.id, ctx.query as any);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.addAccountsToList(ctx.params.id, (ctx.query as any).account_ids);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.delete<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
|
||||||
const data = await client.deleteAccountsFromList(ctx.params.id, (ctx.query as any).account_ids);
|
|
||||||
ctx.body = data.data;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e)
|
|
||||||
console.error(e.response.data)
|
|
||||||
ctx.status = (401);
|
|
||||||
ctx.body = e.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function escapeHTML(str: string) {
|
|
||||||
if (!str) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''')
|
|
||||||
}
|
|
||||||
function nl2br(str: string) {
|
|
||||||
if (!str) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
str = str.replace(/\r\n/g, '<br />')
|
|
||||||
str = str.replace(/(\n|\r)/g, '<br />')
|
|
||||||
return str
|
|
||||||
}
|
|
|
@ -8,8 +8,6 @@ import serverStats from "./server-stats.js";
|
||||||
import queueStats from "./queue-stats.js";
|
import queueStats from "./queue-stats.js";
|
||||||
import userList from "./user-list.js";
|
import userList from "./user-list.js";
|
||||||
import antenna from "./antenna.js";
|
import antenna from "./antenna.js";
|
||||||
import messaging from "./messaging.js";
|
|
||||||
import messagingIndex from "./messaging-index.js";
|
|
||||||
import drive from "./drive.js";
|
import drive from "./drive.js";
|
||||||
import hashtag from "./hashtag.js";
|
import hashtag from "./hashtag.js";
|
||||||
import channel from "./channel.js";
|
import channel from "./channel.js";
|
||||||
|
@ -26,8 +24,6 @@ export default {
|
||||||
queueStats,
|
queueStats,
|
||||||
userList,
|
userList,
|
||||||
antenna,
|
antenna,
|
||||||
messaging,
|
|
||||||
messagingIndex,
|
|
||||||
drive,
|
drive,
|
||||||
hashtag,
|
hashtag,
|
||||||
channel,
|
channel,
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import Channel from "../channel.js";
|
|
||||||
|
|
||||||
export default class extends Channel {
|
|
||||||
public readonly chName = "messagingIndex";
|
|
||||||
public static shouldShare = true;
|
|
||||||
public static requireCredential = true;
|
|
||||||
|
|
||||||
public async init(params: any) {
|
|
||||||
// Subscribe messaging index stream
|
|
||||||
this.subscriber.on(`messagingIndexStream:${this.user!.id}`, (data) => {
|
|
||||||
this.send(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
import {
|
|
||||||
readUserMessagingMessage,
|
|
||||||
readGroupMessagingMessage,
|
|
||||||
deliverReadActivity,
|
|
||||||
} from "../../common/read-messaging-message.js";
|
|
||||||
import Channel from "../channel.js";
|
|
||||||
import { UserGroupJoinings, Users, MessagingMessages } from "@/models/index.js";
|
|
||||||
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
|
||||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
|
||||||
import type { StreamMessages } from "../types.js";
|
|
||||||
|
|
||||||
export default class extends Channel {
|
|
||||||
public readonly chName = "messaging";
|
|
||||||
public static shouldShare = false;
|
|
||||||
public static requireCredential = true;
|
|
||||||
|
|
||||||
private otherpartyId: string | null;
|
|
||||||
private otherparty: User | null;
|
|
||||||
private groupId: string | null;
|
|
||||||
private subCh:
|
|
||||||
| `messagingStream:${User["id"]}-${User["id"]}`
|
|
||||||
| `messagingStream:${UserGroup["id"]}`;
|
|
||||||
private typers: Record<User["id"], Date> = {};
|
|
||||||
private emitTypersIntervalId: ReturnType<typeof setInterval>;
|
|
||||||
|
|
||||||
constructor(id: string, connection: Channel["connection"]) {
|
|
||||||
super(id, connection);
|
|
||||||
this.onEvent = this.onEvent.bind(this);
|
|
||||||
this.onMessage = this.onMessage.bind(this);
|
|
||||||
this.emitTypers = this.emitTypers.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async init(params: any) {
|
|
||||||
this.otherpartyId = params.otherparty;
|
|
||||||
this.otherparty = this.otherpartyId
|
|
||||||
? await Users.findOneByOrFail({ id: this.otherpartyId })
|
|
||||||
: null;
|
|
||||||
this.groupId = params.group;
|
|
||||||
|
|
||||||
// Check joining
|
|
||||||
if (this.groupId) {
|
|
||||||
const joining = await UserGroupJoinings.findOneBy({
|
|
||||||
userId: this.user!.id,
|
|
||||||
userGroupId: this.groupId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emitTypersIntervalId = setInterval(this.emitTypers, 5000);
|
|
||||||
|
|
||||||
this.subCh = this.otherpartyId
|
|
||||||
? `messagingStream:${this.user!.id}-${this.otherpartyId}`
|
|
||||||
: `messagingStream:${this.groupId}`;
|
|
||||||
|
|
||||||
// Subscribe messaging stream
|
|
||||||
this.subscriber.on(this.subCh, this.onEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onEvent(
|
|
||||||
data:
|
|
||||||
| StreamMessages["messaging"]["payload"]
|
|
||||||
| StreamMessages["groupMessaging"]["payload"],
|
|
||||||
) {
|
|
||||||
if (data.type === "typing") {
|
|
||||||
const id = data.body;
|
|
||||||
const begin = this.typers[id] == null;
|
|
||||||
this.typers[id] = new Date();
|
|
||||||
if (begin) {
|
|
||||||
this.emitTypers();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.send(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public onMessage(type: string, body: any) {
|
|
||||||
switch (type) {
|
|
||||||
case "read":
|
|
||||||
if (this.otherpartyId) {
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (this.groupId) {
|
|
||||||
readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async emitTypers() {
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
// Remove not typing users
|
|
||||||
for (const [userId, date] of Object.entries(this.typers)) {
|
|
||||||
if (now.getTime() - date.getTime() > 5000)
|
|
||||||
this.typers[userId] = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const users = await Users.packMany(Object.keys(this.typers), null, {
|
|
||||||
detail: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.send({
|
|
||||||
type: "typers",
|
|
||||||
body: users,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispose() {
|
|
||||||
this.subscriber.off(this.subCh, this.onEvent);
|
|
||||||
|
|
||||||
clearInterval(this.emitTypersIntervalId);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,6 @@ import readNote from "@/services/note/read.js";
|
||||||
import type { User } from "@/models/entities/user.js";
|
import type { User } from "@/models/entities/user.js";
|
||||||
import type { Channel as ChannelModel } from "@/models/entities/channel.js";
|
import type { Channel as ChannelModel } from "@/models/entities/channel.js";
|
||||||
import {
|
import {
|
||||||
Users,
|
|
||||||
Followings,
|
Followings,
|
||||||
Mutings,
|
Mutings,
|
||||||
UserProfiles,
|
UserProfiles,
|
||||||
|
@ -13,20 +12,12 @@ import {
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import type { AccessToken } from "@/models/entities/access-token.js";
|
import type { AccessToken } from "@/models/entities/access-token.js";
|
||||||
import type { UserProfile } from "@/models/entities/user-profile.js";
|
import type { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
import {
|
|
||||||
publishChannelStream,
|
|
||||||
publishGroupMessagingStream,
|
|
||||||
publishMessagingStream,
|
|
||||||
} from "@/services/stream.js";
|
|
||||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
import type { UserGroup } from "@/models/entities/user-group.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
import { readNotification } from "../common/read-notification.js";
|
import { readNotification } from "../common/read-notification.js";
|
||||||
import channels from "./channels/index.js";
|
import channels from "./channels/index.js";
|
||||||
import type Channel from "./channel.js";
|
import type Channel from "./channel.js";
|
||||||
import type { StreamEventEmitter, StreamMessages } from "./types.js";
|
import type { StreamEventEmitter, StreamMessages } from "./types.js";
|
||||||
import { Converter } from "@cutls/megalodon";
|
|
||||||
import { getClient } from "../mastodon/ApiMastodonCompatibleService.js";
|
|
||||||
import { toTextWithReaction } from "../mastodon/endpoints/timeline.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main stream connection
|
* Main stream connection
|
||||||
|
@ -44,27 +35,17 @@ export default class Connection {
|
||||||
private channels: Channel[] = [];
|
private channels: Channel[] = [];
|
||||||
private subscribingNotes: any = {};
|
private subscribingNotes: any = {};
|
||||||
private cachedNotes: Packed<"Note">[] = [];
|
private cachedNotes: Packed<"Note">[] = [];
|
||||||
private isMastodonCompatible: boolean = false;
|
|
||||||
private host: string;
|
|
||||||
private accessToken: string;
|
|
||||||
private currentSubscribe: string[][] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
wsConnection: websocket.connection,
|
wsConnection: websocket.connection,
|
||||||
subscriber: EventEmitter,
|
subscriber: EventEmitter,
|
||||||
user: User | null | undefined,
|
user: User | null | undefined,
|
||||||
token: AccessToken | null | undefined,
|
token: AccessToken | null | undefined,
|
||||||
host: string,
|
|
||||||
accessToken: string,
|
|
||||||
prepareStream: string | undefined,
|
|
||||||
) {
|
) {
|
||||||
console.log("constructor", prepareStream);
|
|
||||||
this.wsConnection = wsConnection;
|
this.wsConnection = wsConnection;
|
||||||
this.subscriber = subscriber;
|
this.subscriber = subscriber;
|
||||||
if (user) this.user = user;
|
if (user) this.user = user;
|
||||||
if (token) this.token = token;
|
if (token) this.token = token;
|
||||||
if (host) this.host = host;
|
|
||||||
if (accessToken) this.accessToken = accessToken;
|
|
||||||
|
|
||||||
this.onWsConnectionMessage = this.onWsConnectionMessage.bind(this);
|
this.onWsConnectionMessage = this.onWsConnectionMessage.bind(this);
|
||||||
this.onUserEvent = this.onUserEvent.bind(this);
|
this.onUserEvent = this.onUserEvent.bind(this);
|
||||||
|
@ -86,13 +67,6 @@ export default class Connection {
|
||||||
|
|
||||||
this.subscriber.on(`user:${this.user.id}`, this.onUserEvent);
|
this.subscriber.on(`user:${this.user.id}`, this.onUserEvent);
|
||||||
}
|
}
|
||||||
console.log("prepare", prepareStream);
|
|
||||||
if (prepareStream) {
|
|
||||||
this.onWsConnectionMessage({
|
|
||||||
type: "utf8",
|
|
||||||
utf8Data: JSON.stringify({ stream: prepareStream, type: "subscribe" }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUserEvent(data: StreamMessages["user"]["payload"]) {
|
private onUserEvent(data: StreamMessages["user"]["payload"]) {
|
||||||
|
@ -145,106 +119,16 @@ export default class Connection {
|
||||||
if (data.type !== "utf8") return;
|
if (data.type !== "utf8") return;
|
||||||
if (data.utf8Data == null) return;
|
if (data.utf8Data == null) return;
|
||||||
|
|
||||||
let objs: Record<string, any>[];
|
let obj: Record<string, any>;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
objs = [JSON.parse(data.utf8Data)];
|
obj = JSON.parse(data.utf8Data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const simpleObj = objs[0];
|
|
||||||
if (simpleObj.stream) {
|
|
||||||
// is Mastodon Compatible
|
|
||||||
this.isMastodonCompatible = true;
|
|
||||||
if (simpleObj.type === "subscribe") {
|
|
||||||
let forSubscribe = [];
|
|
||||||
if (simpleObj.stream === "user") {
|
|
||||||
this.currentSubscribe.push(["user"]);
|
|
||||||
objs = [
|
|
||||||
{
|
|
||||||
type: "connect",
|
|
||||||
body: {
|
|
||||||
channel: "main",
|
|
||||||
id: simpleObj.stream,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "connect",
|
|
||||||
body: {
|
|
||||||
channel: "homeTimeline",
|
|
||||||
id: simpleObj.stream,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const client = getClient(this.host, this.accessToken);
|
|
||||||
try {
|
|
||||||
const tl = await client.getHomeTimeline();
|
|
||||||
for (const t of tl.data) forSubscribe.push(t.id);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.log(e);
|
|
||||||
console.error(e.response.data);
|
|
||||||
}
|
|
||||||
} else if (simpleObj.stream === "public:local") {
|
|
||||||
this.currentSubscribe.push(["public:local"]);
|
|
||||||
objs = [
|
|
||||||
{
|
|
||||||
type: "connect",
|
|
||||||
body: {
|
|
||||||
channel: "localTimeline",
|
|
||||||
id: simpleObj.stream,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const client = getClient(this.host, this.accessToken);
|
|
||||||
const tl = await client.getLocalTimeline();
|
|
||||||
for (const t of tl.data) forSubscribe.push(t.id);
|
|
||||||
} else if (simpleObj.stream === "public") {
|
|
||||||
this.currentSubscribe.push(["public"]);
|
|
||||||
objs = [
|
|
||||||
{
|
|
||||||
type: "connect",
|
|
||||||
body: {
|
|
||||||
channel: "globalTimeline",
|
|
||||||
id: simpleObj.stream,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const client = getClient(this.host, this.accessToken);
|
|
||||||
const tl = await client.getPublicTimeline();
|
|
||||||
for (const t of tl.data) forSubscribe.push(t.id);
|
|
||||||
} else if (simpleObj.stream === "list") {
|
|
||||||
this.currentSubscribe.push(["list", simpleObj.list]);
|
|
||||||
objs = [
|
|
||||||
{
|
|
||||||
type: "connect",
|
|
||||||
body: {
|
|
||||||
channel: "list",
|
|
||||||
id: simpleObj.stream,
|
|
||||||
params: {
|
|
||||||
listId: simpleObj.list,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const client = getClient(this.host, this.accessToken);
|
|
||||||
const tl = await client.getListTimeline(simpleObj.list);
|
|
||||||
for (const t of tl.data) forSubscribe.push(t.id);
|
|
||||||
}
|
|
||||||
for (const s of forSubscribe) {
|
|
||||||
objs.push({
|
|
||||||
type: "s",
|
|
||||||
body: {
|
|
||||||
id: s,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const obj of objs) {
|
|
||||||
const { type, body } = obj;
|
const { type, body } = obj;
|
||||||
console.log(type, body);
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "readNotification":
|
case "readNotification":
|
||||||
this.onReadNotification(body);
|
this.onReadNotification(body);
|
||||||
|
@ -278,16 +162,6 @@ export default class Connection {
|
||||||
this.onChannelMessageRequested(body);
|
this.onChannelMessageRequested(body);
|
||||||
break; // alias
|
break; // alias
|
||||||
|
|
||||||
// 個々のチャンネルではなくルートレベルでこれらのメッセージを受け取る理由は、
|
|
||||||
// クライアントの事情を考慮したとき、入力フォームはノートチャンネルやメッセージのメインコンポーネントとは別
|
|
||||||
// なこともあるため、それらのコンポーネントがそれぞれ各チャンネルに接続するようにするのは面倒なため。
|
|
||||||
case "typingOnChannel":
|
|
||||||
this.typingOnChannel(body.channel);
|
|
||||||
break;
|
|
||||||
case "typingOnMessaging":
|
|
||||||
this.typingOnMessaging(body);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,68 +265,6 @@ export default class Connection {
|
||||||
* クライアントにメッセージ送信
|
* クライアントにメッセージ送信
|
||||||
*/
|
*/
|
||||||
public sendMessageToWs(type: string, payload: any) {
|
public sendMessageToWs(type: string, payload: any) {
|
||||||
console.log(payload, this.isMastodonCompatible);
|
|
||||||
if (this.isMastodonCompatible) {
|
|
||||||
if (payload.type === "note") {
|
|
||||||
this.wsConnection.send(
|
|
||||||
JSON.stringify({
|
|
||||||
stream: [payload.id],
|
|
||||||
event: "update",
|
|
||||||
payload: JSON.stringify(
|
|
||||||
toTextWithReaction(
|
|
||||||
[Converter.note(payload.body, this.host)],
|
|
||||||
this.host,
|
|
||||||
)[0],
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.onSubscribeNote({
|
|
||||||
id: payload.body.id,
|
|
||||||
});
|
|
||||||
} else if (payload.type === "reacted" || payload.type === "unreacted") {
|
|
||||||
// reaction
|
|
||||||
const client = getClient(this.host, this.accessToken);
|
|
||||||
client.getStatus(payload.id).then((data) => {
|
|
||||||
const newPost = toTextWithReaction([data.data], this.host);
|
|
||||||
for (const stream of this.currentSubscribe) {
|
|
||||||
this.wsConnection.send(
|
|
||||||
JSON.stringify({
|
|
||||||
stream,
|
|
||||||
event: "status.update",
|
|
||||||
payload: JSON.stringify(newPost[0]),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (payload.type === "deleted") {
|
|
||||||
// delete
|
|
||||||
for (const stream of this.currentSubscribe) {
|
|
||||||
this.wsConnection.send(
|
|
||||||
JSON.stringify({
|
|
||||||
stream,
|
|
||||||
event: "delete",
|
|
||||||
payload: payload.id,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (payload.type === "unreadNotification") {
|
|
||||||
if (payload.id === "user") {
|
|
||||||
const body = Converter.notification(payload.body, this.host);
|
|
||||||
if (body.type === "reaction") body.type = "favourite";
|
|
||||||
body.status = toTextWithReaction(
|
|
||||||
body.status ? [body.status] : [],
|
|
||||||
"",
|
|
||||||
)[0];
|
|
||||||
this.wsConnection.send(
|
|
||||||
JSON.stringify({
|
|
||||||
stream: ["user"],
|
|
||||||
event: "notification",
|
|
||||||
payload: JSON.stringify(body),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.wsConnection.send(
|
this.wsConnection.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: type,
|
type: type,
|
||||||
|
@ -460,7 +272,6 @@ export default class Connection {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* チャンネルに接続
|
* チャンネルに接続
|
||||||
|
@ -518,30 +329,6 @@ export default class Connection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private typingOnChannel(channel: ChannelModel["id"]) {
|
|
||||||
if (this.user) {
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
} else if (param.group) {
|
|
||||||
publishGroupMessagingStream(param.group, "typing", this.user.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateFollowing() {
|
private async updateFollowing() {
|
||||||
const followings = await Followings.find({
|
const followings = await Followings.find({
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import type { DriveFolder } from "@/models/entities/drive-folder.js";
|
import type { DriveFolder } from "@/models/entities/drive-folder.js";
|
||||||
import { Emoji } from "@/models/entities/emoji.js";
|
import { Emoji } from "@/models/entities/emoji.js";
|
||||||
import type { UserList } from "@/models/entities/user-list.js";
|
import type { UserList } from "@/models/entities/user-list.js";
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
import type { UserGroup } from "@/models/entities/user-group.js";
|
||||||
import type { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
import type { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
|
||||||
import type { Signin } from "@/models/entities/signin.js";
|
import type { Signin } from "@/models/entities/signin.js";
|
||||||
|
@ -86,9 +85,6 @@ export interface MainStreamTypes {
|
||||||
readAllUnreadMentions: undefined;
|
readAllUnreadMentions: undefined;
|
||||||
unreadSpecifiedNote: Note["id"];
|
unreadSpecifiedNote: Note["id"];
|
||||||
readAllUnreadSpecifiedNotes: undefined;
|
readAllUnreadSpecifiedNotes: undefined;
|
||||||
readAllMessagingMessages: undefined;
|
|
||||||
messagingMessage: Packed<"MessagingMessage">;
|
|
||||||
unreadMessagingMessage: Packed<"MessagingMessage">;
|
|
||||||
readAllAntennas: undefined;
|
readAllAntennas: undefined;
|
||||||
unreadAntenna: Antenna;
|
unreadAntenna: Antenna;
|
||||||
readAllAnnouncements: undefined;
|
readAllAnnouncements: undefined;
|
||||||
|
@ -156,28 +152,6 @@ export interface AntennaStreamTypes {
|
||||||
note: Note;
|
note: Note;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessagingStreamTypes {
|
|
||||||
read: MessagingMessage["id"][];
|
|
||||||
typing: User["id"];
|
|
||||||
message: Packed<"MessagingMessage">;
|
|
||||||
deleted: MessagingMessage["id"];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupMessagingStreamTypes {
|
|
||||||
read: {
|
|
||||||
ids: MessagingMessage["id"][];
|
|
||||||
userId: User["id"];
|
|
||||||
};
|
|
||||||
typing: User["id"];
|
|
||||||
message: Packed<"MessagingMessage">;
|
|
||||||
deleted: MessagingMessage["id"];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MessagingIndexStreamTypes {
|
|
||||||
read: MessagingMessage["id"][];
|
|
||||||
message: Packed<"MessagingMessage">;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AdminStreamTypes {
|
export interface AdminStreamTypes {
|
||||||
newAbuseUserReport: {
|
newAbuseUserReport: {
|
||||||
id: AbuseUserReport["id"];
|
id: AbuseUserReport["id"];
|
||||||
|
@ -232,18 +206,6 @@ export type StreamMessages = {
|
||||||
name: `antennaStream:${Antenna["id"]}`;
|
name: `antennaStream:${Antenna["id"]}`;
|
||||||
payload: EventUnionFromDictionary<AntennaStreamTypes>;
|
payload: EventUnionFromDictionary<AntennaStreamTypes>;
|
||||||
};
|
};
|
||||||
messaging: {
|
|
||||||
name: `messagingStream:${User["id"]}-${User["id"]}`;
|
|
||||||
payload: EventUnionFromDictionary<MessagingStreamTypes>;
|
|
||||||
};
|
|
||||||
groupMessaging: {
|
|
||||||
name: `messagingStream:${UserGroup["id"]}`;
|
|
||||||
payload: EventUnionFromDictionary<GroupMessagingStreamTypes>;
|
|
||||||
};
|
|
||||||
messagingIndex: {
|
|
||||||
name: `messagingIndexStream:${User["id"]}`;
|
|
||||||
payload: EventUnionFromDictionary<MessagingIndexStreamTypes>;
|
|
||||||
};
|
|
||||||
admin: {
|
admin: {
|
||||||
name: `adminStream:${User["id"]}`;
|
name: `adminStream:${User["id"]}`;
|
||||||
payload: EventUnionFromDictionary<AdminStreamTypes>;
|
payload: EventUnionFromDictionary<AdminStreamTypes>;
|
||||||
|
|
|
@ -20,8 +20,6 @@ import { createTemp } from "@/misc/create-temp.js";
|
||||||
import { publishMainStream } from "@/services/stream.js";
|
import { publishMainStream } from "@/services/stream.js";
|
||||||
import * as Acct from "@/misc/acct.js";
|
import * as Acct from "@/misc/acct.js";
|
||||||
import { envOption } from "@/env.js";
|
import { envOption } from "@/env.js";
|
||||||
import { koaBody } from 'koa-body';
|
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import activityPub from "./activitypub.js";
|
import activityPub from "./activitypub.js";
|
||||||
import nodeinfo from "./nodeinfo.js";
|
import nodeinfo from "./nodeinfo.js";
|
||||||
import wellKnown from "./well-known.js";
|
import wellKnown from "./well-known.js";
|
||||||
|
@ -135,34 +133,6 @@ router.get("/verify-email/:code", async (ctx) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/oauth/authorize", async (ctx) => {
|
|
||||||
const client_id = ctx.request.query.client_id;
|
|
||||||
console.log(ctx.request.req);
|
|
||||||
ctx.redirect(Buffer.from(client_id?.toString() || '', 'base64').toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post("/oauth/token", async (ctx) => {
|
|
||||||
const body: any = ctx.request.body;
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
|
||||||
const generator = (megalodon as any).default;
|
|
||||||
const client = generator('misskey', BASE_URL, null) as MegalodonInterface;
|
|
||||||
const m = body.code.match(/^[a-zA-Z0-9-]+/);
|
|
||||||
if (!m.length) return { error: 'Invalid code' }
|
|
||||||
try {
|
|
||||||
const atData = await client.fetchAccessToken(null, body.client_secret, m[0]);
|
|
||||||
ctx.body = {
|
|
||||||
access_token: atData.accessToken,
|
|
||||||
token_type: 'Bearer',
|
|
||||||
scope: 'read write follow',
|
|
||||||
created_at: new Date().getTime() / 1000
|
|
||||||
};
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error(err);
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = err.response.data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register router
|
// Register router
|
||||||
app.use(router.routes());
|
app.use(router.routes());
|
||||||
|
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
import type { CacheableUser, User } from "@/models/entities/user.js";
|
|
||||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
|
||||||
import {
|
|
||||||
MessagingMessages,
|
|
||||||
UserGroupJoinings,
|
|
||||||
Mutings,
|
|
||||||
Users,
|
|
||||||
} from "@/models/index.js";
|
|
||||||
import { genId } from "@/misc/gen-id.js";
|
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import {
|
|
||||||
publishMessagingStream,
|
|
||||||
publishMessagingIndexStream,
|
|
||||||
publishMainStream,
|
|
||||||
publishGroupMessagingStream,
|
|
||||||
} from "@/services/stream.js";
|
|
||||||
import { pushNotification } from "@/services/push-notification.js";
|
|
||||||
import { Not } from "typeorm";
|
|
||||||
import type { Note } from "@/models/entities/note.js";
|
|
||||||
import renderNote from "@/remote/activitypub/renderer/note.js";
|
|
||||||
import renderCreate from "@/remote/activitypub/renderer/create.js";
|
|
||||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
|
||||||
import { deliver } from "@/queue/index.js";
|
|
||||||
|
|
||||||
export async function createMessage(
|
|
||||||
user: { id: User["id"]; host: User["host"] },
|
|
||||||
recipientUser: CacheableUser | undefined,
|
|
||||||
recipientGroup: UserGroup | undefined,
|
|
||||||
text: string | null | undefined,
|
|
||||||
file: DriveFile | null,
|
|
||||||
uri?: string,
|
|
||||||
) {
|
|
||||||
const message = {
|
|
||||||
id: genId(),
|
|
||||||
createdAt: new Date(),
|
|
||||||
fileId: file ? file.id : null,
|
|
||||||
recipientId: recipientUser ? recipientUser.id : null,
|
|
||||||
groupId: recipientGroup ? recipientGroup.id : null,
|
|
||||||
text: text ? text.trim() : null,
|
|
||||||
userId: user.id,
|
|
||||||
isRead: false,
|
|
||||||
reads: [] as any[],
|
|
||||||
uri,
|
|
||||||
} as MessagingMessage;
|
|
||||||
|
|
||||||
await MessagingMessages.insert(message);
|
|
||||||
|
|
||||||
const messageObj = await MessagingMessages.pack(message);
|
|
||||||
|
|
||||||
if (recipientUser) {
|
|
||||||
if (Users.isLocalUser(user)) {
|
|
||||||
// 自分のストリーム
|
|
||||||
publishMessagingStream(
|
|
||||||
message.userId,
|
|
||||||
recipientUser.id,
|
|
||||||
"message",
|
|
||||||
messageObj,
|
|
||||||
);
|
|
||||||
publishMessagingIndexStream(message.userId, "message", messageObj);
|
|
||||||
publishMainStream(message.userId, "messagingMessage", messageObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Users.isLocalUser(recipientUser)) {
|
|
||||||
// 相手のストリーム
|
|
||||||
publishMessagingStream(
|
|
||||||
recipientUser.id,
|
|
||||||
message.userId,
|
|
||||||
"message",
|
|
||||||
messageObj,
|
|
||||||
);
|
|
||||||
publishMessagingIndexStream(recipientUser.id, "message", messageObj);
|
|
||||||
publishMainStream(recipientUser.id, "messagingMessage", messageObj);
|
|
||||||
}
|
|
||||||
} else if (recipientGroup) {
|
|
||||||
// グループのストリーム
|
|
||||||
publishGroupMessagingStream(recipientGroup.id, "message", messageObj);
|
|
||||||
|
|
||||||
// メンバーのストリーム
|
|
||||||
const joinings = await UserGroupJoinings.findBy({
|
|
||||||
userGroupId: recipientGroup.id,
|
|
||||||
});
|
|
||||||
for (const joining of joinings) {
|
|
||||||
publishMessagingIndexStream(joining.userId, "message", messageObj);
|
|
||||||
publishMainStream(joining.userId, "messagingMessage", messageObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する
|
|
||||||
setTimeout(async () => {
|
|
||||||
const freshMessage = await MessagingMessages.findOneBy({ id: message.id });
|
|
||||||
if (freshMessage == null) return; // メッセージが削除されている場合もある
|
|
||||||
|
|
||||||
if (recipientUser && Users.isLocalUser(recipientUser)) {
|
|
||||||
if (freshMessage.isRead) return; // 既読
|
|
||||||
|
|
||||||
//#region ただしミュートされているなら発行しない
|
|
||||||
const mute = await Mutings.findBy({
|
|
||||||
muterId: recipientUser.id,
|
|
||||||
});
|
|
||||||
if (mute.map((m) => m.muteeId).includes(user.id)) return;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
publishMainStream(recipientUser.id, "unreadMessagingMessage", messageObj);
|
|
||||||
pushNotification(recipientUser.id, "unreadMessagingMessage", messageObj);
|
|
||||||
} else if (recipientGroup) {
|
|
||||||
const joinings = await UserGroupJoinings.findBy({
|
|
||||||
userGroupId: recipientGroup.id,
|
|
||||||
userId: Not(user.id),
|
|
||||||
});
|
|
||||||
for (const joining of joinings) {
|
|
||||||
if (freshMessage.reads.includes(joining.userId)) return; // 既読
|
|
||||||
publishMainStream(joining.userId, "unreadMessagingMessage", messageObj);
|
|
||||||
pushNotification(joining.userId, "unreadMessagingMessage", messageObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
if (
|
|
||||||
recipientUser &&
|
|
||||||
Users.isLocalUser(user) &&
|
|
||||||
Users.isRemoteUser(recipientUser)
|
|
||||||
) {
|
|
||||||
const note = {
|
|
||||||
id: message.id,
|
|
||||||
createdAt: message.createdAt,
|
|
||||||
fileIds: message.fileId ? [message.fileId] : [],
|
|
||||||
text: message.text,
|
|
||||||
userId: message.userId,
|
|
||||||
visibility: "specified",
|
|
||||||
mentions: [recipientUser].map((u) => u.id),
|
|
||||||
mentionedRemoteUsers: JSON.stringify(
|
|
||||||
[recipientUser].map((u) => ({
|
|
||||||
uri: u.uri,
|
|
||||||
username: u.username,
|
|
||||||
host: u.host,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
} as Note;
|
|
||||||
|
|
||||||
const activity = renderActivity(
|
|
||||||
renderCreate(await renderNote(note, false, true), note),
|
|
||||||
);
|
|
||||||
|
|
||||||
deliver(user, activity, recipientUser.inbox);
|
|
||||||
}
|
|
||||||
return messageObj;
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
import config from "@/config/index.js";
|
|
||||||
import { MessagingMessages, Users } from "@/models/index.js";
|
|
||||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
|
||||||
import {
|
|
||||||
publishGroupMessagingStream,
|
|
||||||
publishMessagingStream,
|
|
||||||
} from "@/services/stream.js";
|
|
||||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
|
||||||
import renderDelete from "@/remote/activitypub/renderer/delete.js";
|
|
||||||
import renderTombstone from "@/remote/activitypub/renderer/tombstone.js";
|
|
||||||
import { deliver } from "@/queue/index.js";
|
|
||||||
|
|
||||||
export async function deleteMessage(message: MessagingMessage) {
|
|
||||||
await MessagingMessages.delete(message.id);
|
|
||||||
postDeleteMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function postDeleteMessage(message: MessagingMessage) {
|
|
||||||
if (message.recipientId) {
|
|
||||||
const user = await Users.findOneByOrFail({ id: message.userId });
|
|
||||||
const recipient = await Users.findOneByOrFail({ id: message.recipientId });
|
|
||||||
|
|
||||||
if (Users.isLocalUser(user))
|
|
||||||
publishMessagingStream(
|
|
||||||
message.userId,
|
|
||||||
message.recipientId,
|
|
||||||
"deleted",
|
|
||||||
message.id,
|
|
||||||
);
|
|
||||||
if (Users.isLocalUser(recipient))
|
|
||||||
publishMessagingStream(
|
|
||||||
message.recipientId,
|
|
||||||
message.userId,
|
|
||||||
"deleted",
|
|
||||||
message.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) {
|
|
||||||
const activity = renderActivity(
|
|
||||||
renderDelete(
|
|
||||||
renderTombstone(`${config.url}/notes/${message.id}`),
|
|
||||||
user,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
deliver(user, activity, recipient.inbox);
|
|
||||||
}
|
|
||||||
} else if (message.groupId) {
|
|
||||||
publishGroupMessagingStream(message.groupId, "deleted", message.id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,11 +8,8 @@ import { getNoteSummary } from "@/misc/get-note-summary.js";
|
||||||
// Defined also packages/sw/types.ts#L14-L21
|
// Defined also packages/sw/types.ts#L14-L21
|
||||||
type pushNotificationsTypes = {
|
type pushNotificationsTypes = {
|
||||||
notification: Packed<"Notification">;
|
notification: Packed<"Notification">;
|
||||||
unreadMessagingMessage: Packed<"MessagingMessage">;
|
|
||||||
readNotifications: { notificationIds: string[] };
|
readNotifications: { notificationIds: string[] };
|
||||||
readAllNotifications: undefined;
|
readAllNotifications: undefined;
|
||||||
readAllMessagingMessages: undefined;
|
|
||||||
readAllMessagingMessagesOfARoom: { userId: string } | { groupId: string };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// プッシュメッセージサーバーには文字数制限があるため、内容を削減します
|
// プッシュメッセージサーバーには文字数制限があるため、内容を削減します
|
||||||
|
|
|
@ -13,11 +13,8 @@ import type {
|
||||||
BroadcastTypes,
|
BroadcastTypes,
|
||||||
ChannelStreamTypes,
|
ChannelStreamTypes,
|
||||||
DriveStreamTypes,
|
DriveStreamTypes,
|
||||||
GroupMessagingStreamTypes,
|
|
||||||
InternalStreamTypes,
|
InternalStreamTypes,
|
||||||
MainStreamTypes,
|
MainStreamTypes,
|
||||||
MessagingIndexStreamTypes,
|
|
||||||
MessagingStreamTypes,
|
|
||||||
NoteStreamTypes,
|
NoteStreamTypes,
|
||||||
UserListStreamTypes,
|
UserListStreamTypes,
|
||||||
UserStreamTypes,
|
UserStreamTypes,
|
||||||
|
@ -146,47 +143,6 @@ class Publisher {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
public publishMessagingStream = <K extends keyof MessagingStreamTypes>(
|
|
||||||
userId: User["id"],
|
|
||||||
otherpartyId: User["id"],
|
|
||||||
type: K,
|
|
||||||
value?: MessagingStreamTypes[K],
|
|
||||||
): void => {
|
|
||||||
this.publish(
|
|
||||||
`messagingStream:${userId}-${otherpartyId}`,
|
|
||||||
type,
|
|
||||||
typeof value === "undefined" ? null : value,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
public publishGroupMessagingStream = <
|
|
||||||
K extends keyof GroupMessagingStreamTypes,
|
|
||||||
>(
|
|
||||||
groupId: UserGroup["id"],
|
|
||||||
type: K,
|
|
||||||
value?: GroupMessagingStreamTypes[K],
|
|
||||||
): void => {
|
|
||||||
this.publish(
|
|
||||||
`messagingStream:${groupId}`,
|
|
||||||
type,
|
|
||||||
typeof value === "undefined" ? null : value,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
public publishMessagingIndexStream = <
|
|
||||||
K extends keyof MessagingIndexStreamTypes,
|
|
||||||
>(
|
|
||||||
userId: User["id"],
|
|
||||||
type: K,
|
|
||||||
value?: MessagingIndexStreamTypes[K],
|
|
||||||
): void => {
|
|
||||||
this.publish(
|
|
||||||
`messagingIndexStream:${userId}`,
|
|
||||||
type,
|
|
||||||
typeof value === "undefined" ? null : value,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
public publishNotesStream = (note: Note): void => {
|
public publishNotesStream = (note: Note): void => {
|
||||||
this.publish("notesStream", null, note);
|
this.publish("notesStream", null, note);
|
||||||
};
|
};
|
||||||
|
@ -218,9 +174,4 @@ export const publishNotesStream = publisher.publishNotesStream;
|
||||||
export const publishChannelStream = publisher.publishChannelStream;
|
export const publishChannelStream = publisher.publishChannelStream;
|
||||||
export const publishUserListStream = publisher.publishUserListStream;
|
export const publishUserListStream = publisher.publishUserListStream;
|
||||||
export const publishAntennaStream = publisher.publishAntennaStream;
|
export const publishAntennaStream = publisher.publishAntennaStream;
|
||||||
export const publishMessagingStream = publisher.publishMessagingStream;
|
|
||||||
export const publishGroupMessagingStream =
|
|
||||||
publisher.publishGroupMessagingStream;
|
|
||||||
export const publishMessagingIndexStream =
|
|
||||||
publisher.publishMessagingIndexStream;
|
|
||||||
export const publishAdminStream = publisher.publishAdminStream;
|
export const publishAdminStream = publisher.publishAdminStream;
|
||||||
|
|
|
@ -460,15 +460,6 @@ import { getAccountFromId } from "@/scripts/get-account-from-id";
|
||||||
updateAccount({ hasUnreadSpecifiedNotes: false });
|
updateAccount({ hasUnreadSpecifiedNotes: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on("readAllMessagingMessages", () => {
|
|
||||||
updateAccount({ hasUnreadMessagingMessage: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
main.on("unreadMessagingMessage", () => {
|
|
||||||
updateAccount({ hasUnreadMessagingMessage: true });
|
|
||||||
sound.play("chatBg");
|
|
||||||
});
|
|
||||||
|
|
||||||
main.on("readAllAntennas", () => {
|
main.on("readAllAntennas", () => {
|
||||||
updateAccount({ hasUnreadAntenna: false });
|
updateAccount({ hasUnreadAntenna: false });
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,13 +18,6 @@ export const navbarItemDef = reactive({
|
||||||
indicated: computed(() => $i?.hasUnreadNotification),
|
indicated: computed(() => $i?.hasUnreadNotification),
|
||||||
to: "/my/notifications",
|
to: "/my/notifications",
|
||||||
},
|
},
|
||||||
messaging: {
|
|
||||||
title: "messaging",
|
|
||||||
icon: "ph-chats-teardrop-bold ph-lg",
|
|
||||||
show: computed(() => $i != null),
|
|
||||||
indicated: computed(() => $i?.hasUnreadMessagingMessage),
|
|
||||||
to: "/my/messaging",
|
|
||||||
},
|
|
||||||
drive: {
|
drive: {
|
||||||
title: "drive",
|
title: "drive",
|
||||||
icon: "ph-cloud-bold ph-lg",
|
icon: "ph-cloud-bold ph-lg",
|
||||||
|
|
|
@ -1,220 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<div>
|
|
||||||
<MkSpacer :content-max="800">
|
|
||||||
<swiper
|
|
||||||
:modules="[Virtual]"
|
|
||||||
:space-between="20"
|
|
||||||
:virtual="true"
|
|
||||||
:allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)"
|
|
||||||
@swiper="setSwiperRef"
|
|
||||||
@slide-change="onSlideChange"
|
|
||||||
>
|
|
||||||
<swiper-slide>
|
|
||||||
<div class="_content yweeujhr dms">
|
|
||||||
<MkButton primary class="start" @click="startUser"><i class="ph-plus-bold ph-lg"></i> {{ i18n.ts.startMessaging }}</MkButton>
|
|
||||||
<MkPagination v-slot="{items}" :pagination="dmsPagination">
|
|
||||||
<MkChatPreview v-for="message in items" :key="message.id" class="yweeujhr message _block" :message="message"/>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
|
||||||
</swiper-slide>
|
|
||||||
<swiper-slide>
|
|
||||||
<div class="_content yweeujhr groups">
|
|
||||||
<div class="groupsbuttons">
|
|
||||||
<MkButton primary class="start" :link="true" to="/my/groups"><i class="ph-user-circle-gear-bold ph-lg"></i> {{ i18n.ts.manageGroups }}</MkButton>
|
|
||||||
<MkButton primary class="start" @click="startGroup"><i class="ph-plus-bold ph-lg"></i> {{ i18n.ts.startMessaging }}</MkButton>
|
|
||||||
</div>
|
|
||||||
<MkPagination v-slot="{items}" :pagination="groupsPagination">
|
|
||||||
<MkChatPreview v-for="message in items" :key="message.id" class="yweeujhr message _block" :message="message"/>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
|
||||||
</swiper-slide>
|
|
||||||
</swiper>
|
|
||||||
</MkSpacer>
|
|
||||||
</div>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { markRaw, onMounted, onUnmounted, watch } from 'vue';
|
|
||||||
import * as Acct from 'calckey-js/built/acct';
|
|
||||||
import { Virtual } from 'swiper';
|
|
||||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
|
||||||
import MkButton from '@/components/MkButton.vue';
|
|
||||||
import MkChatPreview from '@/components/MkChatPreview.vue';
|
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { stream } from '@/stream';
|
|
||||||
import { useRouter } from '@/router';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
import { $i } from '@/account';
|
|
||||||
import { deviceKind } from '@/scripts/device-kind';
|
|
||||||
import { defaultStore } from '@/store';
|
|
||||||
import 'swiper/scss';
|
|
||||||
import 'swiper/scss/virtual';
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
let messages = $ref([]);
|
|
||||||
let connection = $ref(null);
|
|
||||||
|
|
||||||
const tabs = ['dms', 'groups'];
|
|
||||||
let tab = $ref(tabs[0]);
|
|
||||||
watch($$(tab), () => (syncSlide(tabs.indexOf(tab))));
|
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
|
||||||
asFullButton: true,
|
|
||||||
icon: 'ph-plus-bold ph-lg',
|
|
||||||
text: i18n.ts.addUser,
|
|
||||||
handler: startMenu,
|
|
||||||
}]);
|
|
||||||
|
|
||||||
const headerTabs = $computed(() => [{
|
|
||||||
key: 'dms',
|
|
||||||
title: i18n.ts._messaging.dms,
|
|
||||||
icon: 'ph-user-bold ph-lg',
|
|
||||||
}, {
|
|
||||||
key: 'groups',
|
|
||||||
title: i18n.ts._messaging.groups,
|
|
||||||
icon: 'ph-users-three-bold ph-lg',
|
|
||||||
}]);
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.messaging,
|
|
||||||
icon: 'ph-chats-teardrop-bold ph-lg',
|
|
||||||
});
|
|
||||||
|
|
||||||
const dmsPagination = {
|
|
||||||
endpoint: 'messaging/history' as const,
|
|
||||||
limit: 15,
|
|
||||||
params: {
|
|
||||||
group: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const groupsPagination = {
|
|
||||||
endpoint: 'messaging/history' as const,
|
|
||||||
limit: 5,
|
|
||||||
params: {
|
|
||||||
group: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function onMessage(message): void {
|
|
||||||
if (message.recipientId) {
|
|
||||||
messages = messages.filter(m => !(
|
|
||||||
(m.recipientId === message.recipientId && m.userId === message.userId) ||
|
|
||||||
(m.recipientId === message.userId && m.userId === message.recipientId)));
|
|
||||||
|
|
||||||
messages.unshift(message);
|
|
||||||
} else if (message.groupId) {
|
|
||||||
messages = messages.filter(m => m.groupId !== message.groupId);
|
|
||||||
messages.unshift(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRead(ids): void {
|
|
||||||
for (const id of ids) {
|
|
||||||
const found = messages.find(m => m.id === id);
|
|
||||||
if (found) {
|
|
||||||
if (found.recipientId) {
|
|
||||||
found.isRead = true;
|
|
||||||
} else if (found.groupId) {
|
|
||||||
found.reads.push($i.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function startMenu(ev) {
|
|
||||||
os.popupMenu([{
|
|
||||||
text: i18n.ts.messagingWithUser,
|
|
||||||
icon: 'ph-user-bold ph-lg',
|
|
||||||
action: () => { startUser(); },
|
|
||||||
}, {
|
|
||||||
text: i18n.ts.messagingWithGroup,
|
|
||||||
icon: 'ph-users-three-bold ph-lg',
|
|
||||||
action: () => { startGroup(); },
|
|
||||||
}], ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function startUser(): void {
|
|
||||||
os.selectUser().then(user => {
|
|
||||||
router.push(`/my/messaging/${Acct.toString(user)}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startGroup(): void {
|
|
||||||
const groups1 = await os.api('users/groups/owned');
|
|
||||||
const groups2 = await os.api('users/groups/joined');
|
|
||||||
if (groups1.length === 0 && groups2.length === 0) {
|
|
||||||
os.alert({
|
|
||||||
type: 'warning',
|
|
||||||
title: i18n.ts.youHaveNoGroups,
|
|
||||||
text: i18n.ts.joinOrCreateGroup,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { canceled, result: group } = await os.select({
|
|
||||||
title: i18n.ts.group,
|
|
||||||
items: groups1.concat(groups2).map(group => ({
|
|
||||||
value: group, text: group.name,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
router.push(`/my/messaging/group/${group.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let swiperRef = null;
|
|
||||||
|
|
||||||
function setSwiperRef(swiper) {
|
|
||||||
swiperRef = swiper;
|
|
||||||
syncSlide(tabs.indexOf(tab));
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSlideChange() {
|
|
||||||
tab = tabs[swiperRef.activeIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncSlide(index) {
|
|
||||||
swiperRef.slideTo(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
syncSlide(tabs.indexOf(swiperRef.activeIndex));
|
|
||||||
|
|
||||||
connection = markRaw(stream.useChannel('messagingIndex'));
|
|
||||||
|
|
||||||
connection.on('message', onMessage);
|
|
||||||
connection.on('read', onRead);
|
|
||||||
|
|
||||||
os.api('messaging/history', { group: false, limit: 5 }).then(userMessages => {
|
|
||||||
os.api('messaging/history', { group: true, limit: 5 }).then(groupMessages => {
|
|
||||||
const _messages = userMessages.concat(groupMessages);
|
|
||||||
_messages.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
||||||
messages = _messages;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (connection) connection.dispose();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.yweeujhr {
|
|
||||||
> .start {
|
|
||||||
margin: 0 auto var(--margin) auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .groupsbuttons {
|
|
||||||
max-width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,361 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="pemppnzi _block"
|
|
||||||
@dragover.stop="onDragover"
|
|
||||||
@drop.stop="onDrop"
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
ref="textEl"
|
|
||||||
v-model="text"
|
|
||||||
:placeholder="i18n.ts.inputMessageHere"
|
|
||||||
@keydown="onKeydown"
|
|
||||||
@compositionupdate="onCompositionUpdate"
|
|
||||||
@paste="onPaste"
|
|
||||||
></textarea>
|
|
||||||
<footer>
|
|
||||||
<div v-if="file" class="file" @click="file = null">{{ file.name }}</div>
|
|
||||||
<div class="buttons">
|
|
||||||
<button class="_button" @click="chooseFile"><i class="ph-upload-bold ph-lg"></i></button>
|
|
||||||
<button class="_button" @click="insertEmoji"><i class="ph-smiley-bold ph-lg"></i></button>
|
|
||||||
<button class="send _button" :disabled="!canSend || sending" :title="i18n.ts.send" @click="send">
|
|
||||||
<template v-if="!sending"><i class="ph-paper-plane-tilt-bold ph-lg"></i></template><template v-if="sending"><i class="ph-circle-notch-bold ph-lg fa-pulse ph-fw ph-lg"></i></template>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
<input ref="fileEl" type="file" @change="onChangeFile"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, watch } from 'vue';
|
|
||||||
import * as Misskey from 'calckey-js';
|
|
||||||
import autosize from 'autosize';
|
|
||||||
//import insertTextAtCursor from 'insert-text-at-cursor';
|
|
||||||
import { throttle } from 'throttle-debounce';
|
|
||||||
import { Autocomplete } from '@/scripts/autocomplete';
|
|
||||||
import { formatTimeString } from '@/scripts/format-time-string';
|
|
||||||
import { selectFile } from '@/scripts/select-file';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { stream } from '@/stream';
|
|
||||||
import { defaultStore } from '@/store';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { uploadFile } from '@/scripts/upload';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
user?: Misskey.entities.UserDetailed | null;
|
|
||||||
group?: Misskey.entities.UserGroup | null;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let textEl = $ref<HTMLTextAreaElement>();
|
|
||||||
let fileEl = $ref<HTMLInputElement>();
|
|
||||||
|
|
||||||
let text = $ref<string>('');
|
|
||||||
let file = $ref<Misskey.entities.DriveFile | null>(null);
|
|
||||||
let sending = $ref(false);
|
|
||||||
const typing = throttle(3000, () => {
|
|
||||||
stream.send('typingOnMessaging', props.user ? { partner: props.user.id } : { group: props.group?.id });
|
|
||||||
});
|
|
||||||
|
|
||||||
let draftKey = $computed(() => props.user ? 'user:' + props.user.id : 'group:' + props.group?.id);
|
|
||||||
let canSend = $computed(() => (text != null && text !== '') || file != null);
|
|
||||||
|
|
||||||
watch([$$(text), $$(file)], saveDraft);
|
|
||||||
|
|
||||||
async function onPaste(ev: ClipboardEvent) {
|
|
||||||
if (!ev.clipboardData) return;
|
|
||||||
|
|
||||||
const clipboardData = ev.clipboardData;
|
|
||||||
const items = clipboardData.items;
|
|
||||||
|
|
||||||
if (items.length === 1) {
|
|
||||||
if (items[0].kind === 'file') {
|
|
||||||
const pastedFile = items[0].getAsFile();
|
|
||||||
if (!pastedFile) return;
|
|
||||||
const lio = pastedFile.name.lastIndexOf('.');
|
|
||||||
const ext = lio >= 0 ? pastedFile.name.slice(lio) : '';
|
|
||||||
const formatted = formatTimeString(new Date(pastedFile.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, '1') + ext;
|
|
||||||
if (formatted) upload(pastedFile, formatted);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (items[0].kind === 'file') {
|
|
||||||
os.alert({
|
|
||||||
type: 'error',
|
|
||||||
text: i18n.ts.onlyOneFileCanBeAttached,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDragover(ev: DragEvent) {
|
|
||||||
if (!ev.dataTransfer) return;
|
|
||||||
|
|
||||||
const isFile = ev.dataTransfer.items[0].kind === 'file';
|
|
||||||
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
|
||||||
if (isFile || isDriveFile) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDrop(ev: DragEvent): void {
|
|
||||||
if (!ev.dataTransfer) return;
|
|
||||||
|
|
||||||
// ファイルだったら
|
|
||||||
if (ev.dataTransfer.files.length === 1) {
|
|
||||||
ev.preventDefault();
|
|
||||||
upload(ev.dataTransfer.files[0]);
|
|
||||||
return;
|
|
||||||
} else if (ev.dataTransfer.files.length > 1) {
|
|
||||||
ev.preventDefault();
|
|
||||||
os.alert({
|
|
||||||
type: 'error',
|
|
||||||
text: i18n.ts.onlyOneFileCanBeAttached,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#region ドライブのファイル
|
|
||||||
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
|
||||||
if (driveFile != null && driveFile !== '') {
|
|
||||||
file = JSON.parse(driveFile);
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
function onKeydown(ev: KeyboardEvent) {
|
|
||||||
typing();
|
|
||||||
let sendOnEnter = localStorage.getItem('enterSendsMessage') === 'true' || defaultStore.state.enterSendsMessage;
|
|
||||||
if (sendOnEnter) {
|
|
||||||
if ((ev.key === 'Enter') && (ev.ctrlKey || ev.metaKey)) {
|
|
||||||
textEl.value += '\n';
|
|
||||||
}
|
|
||||||
else if (ev.key === 'Enter' && !ev.shiftKey && !('ontouchstart' in document.documentElement) && canSend) {
|
|
||||||
ev.preventDefault();
|
|
||||||
send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ((ev.key === 'Enter') && (ev.ctrlKey || ev.metaKey) && canSend) {
|
|
||||||
ev.preventDefault();
|
|
||||||
send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCompositionUpdate() {
|
|
||||||
typing();
|
|
||||||
}
|
|
||||||
|
|
||||||
function chooseFile(ev: MouseEvent) {
|
|
||||||
selectFile(ev.currentTarget ?? ev.target, i18n.ts.selectFile).then(selectedFile => {
|
|
||||||
file = selectedFile;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChangeFile() {
|
|
||||||
if (fileEl.files![0]) upload(fileEl.files[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function upload(fileToUpload: File, name?: string) {
|
|
||||||
uploadFile(fileToUpload, defaultStore.state.uploadFolder, name).then(res => {
|
|
||||||
file = res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function send() {
|
|
||||||
sending = true;
|
|
||||||
os.api('messaging/messages/create', {
|
|
||||||
userId: props.user ? props.user.id : undefined,
|
|
||||||
groupId: props.group ? props.group.id : undefined,
|
|
||||||
text: text ? text : undefined,
|
|
||||||
fileId: file ? file.id : undefined,
|
|
||||||
}).then(message => {
|
|
||||||
clear();
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
}).then(() => {
|
|
||||||
sending = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
text = '';
|
|
||||||
file = null;
|
|
||||||
deleteDraft();
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveDraft() {
|
|
||||||
const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
|
|
||||||
|
|
||||||
drafts[draftKey] = {
|
|
||||||
updatedAt: new Date(),
|
|
||||||
data: {
|
|
||||||
text: text,
|
|
||||||
file: file,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
localStorage.setItem('message_drafts', JSON.stringify(drafts));
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteDraft() {
|
|
||||||
const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
|
|
||||||
|
|
||||||
delete drafts[draftKey];
|
|
||||||
|
|
||||||
localStorage.setItem('message_drafts', JSON.stringify(drafts));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function insertEmoji(ev: MouseEvent) {
|
|
||||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
autosize(textEl);
|
|
||||||
|
|
||||||
// TODO: detach when unmount
|
|
||||||
new Autocomplete(textEl, $$(text));
|
|
||||||
|
|
||||||
// 書きかけの投稿を復元
|
|
||||||
const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[draftKey];
|
|
||||||
if (draft) {
|
|
||||||
text = draft.data.text;
|
|
||||||
file = draft.data.file;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
file,
|
|
||||||
upload,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.pemppnzi {
|
|
||||||
position: relative;
|
|
||||||
margin-top: 1rem;
|
|
||||||
|
|
||||||
> textarea {
|
|
||||||
cursor: auto;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
min-width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
min-height: 80px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px 16px 0 16px;
|
|
||||||
resize: none;
|
|
||||||
font-size: 1em;
|
|
||||||
font-family: inherit;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
background: transparent;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: var(--fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
position: sticky;
|
|
||||||
bottom: 0;
|
|
||||||
background: var(--panel);
|
|
||||||
|
|
||||||
> .file {
|
|
||||||
padding: 8px;
|
|
||||||
color: var(--fg);
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.files {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 8px;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
> li {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 0;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
background-color: #eee;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center center;
|
|
||||||
background-size: cover;
|
|
||||||
cursor: move;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> .remove {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .remove {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
right: -6px;
|
|
||||||
top: -6px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
._button {
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: normal;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.1s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: var(--accentDarken);
|
|
||||||
transition: color 0s ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .send {
|
|
||||||
margin-left: auto;
|
|
||||||
color: var(--accent);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--accentLighten);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: var(--accentDarken);
|
|
||||||
transition: color 0s ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=file] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,329 +0,0 @@
|
||||||
<template>
|
|
||||||
<div v-size="{ max: [400, 500] }" class="thvuemwp" :class="{ isMe }">
|
|
||||||
<MkAvatar v-if="!isMe" class="avatar" :user="message.user" :show-indicator="true"/>
|
|
||||||
<div class="content">
|
|
||||||
<div class="balloon" :class="{ noText: message.text == null }">
|
|
||||||
<button v-if="isMe" class="delete-button" :title="i18n.ts.delete" @click="del">
|
|
||||||
<img src="/client-assets/remove.png" alt="Delete"/>
|
|
||||||
</button>
|
|
||||||
<div v-if="!message.isDeleted" class="content">
|
|
||||||
<Mfm v-if="message.text" ref="text" class="text" :text="message.text" :i="$i"/>
|
|
||||||
</div>
|
|
||||||
<div v-else class="content">
|
|
||||||
<p class="is-deleted">{{ i18n.ts.deleted }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="message.file" class="file" width="400px">
|
|
||||||
<XMediaList v-if="message.file.type.split('/')[0] == 'image' || message.file.type.split('/')[0] == 'video'" :in-dm="true" width="400px" :media-list="[message.file]" style="border-radius: 5px"/>
|
|
||||||
<a v-else :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name">
|
|
||||||
<p>{{ message.file.name }}</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/>
|
|
||||||
<footer>
|
|
||||||
<template v-if="isGroup">
|
|
||||||
<span v-if="message.reads.length > 0" class="read">{{ i18n.ts.messageRead }} {{ message.reads.length }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<span v-if="isMe && message.isRead" class="read">{{ i18n.ts.messageRead }}</span>
|
|
||||||
</template>
|
|
||||||
<MkTime :time="message.createdAt"/>
|
|
||||||
<template v-if="message.is_edited"><i class="ph-pencil-bold ph-lg"></i></template>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { } from 'vue';
|
|
||||||
import * as mfm from 'mfm-js';
|
|
||||||
import type * as Misskey from 'calckey-js';
|
|
||||||
import XMediaList from '@/components/MkMediaList.vue';
|
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
|
||||||
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { $i } from '@/account';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
message: Misskey.entities.MessagingMessage;
|
|
||||||
isGroup?: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const isMe = $computed(() => props.message.userId === $i?.id);
|
|
||||||
const urls = $computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []);
|
|
||||||
|
|
||||||
function del(): void {
|
|
||||||
os.api('messaging/messages/delete', {
|
|
||||||
messageId: props.message.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.thvuemwp {
|
|
||||||
$me-balloon-color: var(--accent);
|
|
||||||
--plyr-color-main: var(--accent);
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
background-color: transparent;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> .avatar {
|
|
||||||
position: sticky;
|
|
||||||
top: calc(var(--stickyTop, 0px) + 20px);
|
|
||||||
display: block;
|
|
||||||
width: 45px;
|
|
||||||
height: 45px;
|
|
||||||
transition: all 0.1s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
> .balloon {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0;
|
|
||||||
min-height: 38px;
|
|
||||||
border-radius: 16px;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
& + * {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> .delete-button {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .delete-button {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: -4px;
|
|
||||||
right: -4px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
> img {
|
|
||||||
vertical-align: bottom;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
> .is-deleted {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
font-size: 1em;
|
|
||||||
color: rgba(#000, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 18px;
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
word-break: break-word;
|
|
||||||
font-size: 1em;
|
|
||||||
color: rgba(#000, 0.8);
|
|
||||||
|
|
||||||
& + .file {
|
|
||||||
> a {
|
|
||||||
border-radius: 0 0 16px 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .file {
|
|
||||||
> a {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
> p {
|
|
||||||
background: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
max-height: 512px;
|
|
||||||
object-fit: contain;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
> p {
|
|
||||||
padding: 30px;
|
|
||||||
text-align: center;
|
|
||||||
color: #6e6a86;
|
|
||||||
background: #ddd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> footer {
|
|
||||||
display: block;
|
|
||||||
margin: 2px 0 0 0;
|
|
||||||
font-size: 0.65em;
|
|
||||||
|
|
||||||
> .read {
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> i {
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.isMe) {
|
|
||||||
padding-left: var(--margin);
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 32px;
|
|
||||||
|
|
||||||
> .balloon {
|
|
||||||
$color: var(--X4);
|
|
||||||
background: $color;
|
|
||||||
|
|
||||||
&.noText {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.noText):before {
|
|
||||||
left: -14px;
|
|
||||||
border-top: solid 8px transparent;
|
|
||||||
border-right: solid 8px $color;
|
|
||||||
border-bottom: solid 8px transparent;
|
|
||||||
border-left: solid 8px transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
> .text {
|
|
||||||
color: var(--fg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> footer {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.isMe {
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
padding-right: var(--margin);
|
|
||||||
right: var(--margin); // 削除時にposition: absoluteになったときに使う
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
padding-right: 16px;
|
|
||||||
padding-left: 32px;
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
> .balloon {
|
|
||||||
background: $me-balloon-color;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
::selection {
|
|
||||||
color: var(--accent);
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.noText {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.noText):before {
|
|
||||||
right: -14px;
|
|
||||||
left: auto;
|
|
||||||
border-top: solid 8px transparent;
|
|
||||||
border-right: solid 8px transparent;
|
|
||||||
border-bottom: solid 8px transparent;
|
|
||||||
border-left: solid 8px $me-balloon-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
|
|
||||||
> p.is-deleted {
|
|
||||||
color: rgba(#fff, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
&, ::v-deep(*) {
|
|
||||||
color: var(--fgOnAccent) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> footer {
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
> .read {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.max-width_400px {
|
|
||||||
> .avatar {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
> .balloon {
|
|
||||||
> .content {
|
|
||||||
> .text {
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.max-width_500px {
|
|
||||||
> .content {
|
|
||||||
> .balloon {
|
|
||||||
> .content {
|
|
||||||
> .text {
|
|
||||||
padding: 8px 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,400 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
ref="rootEl"
|
|
||||||
class="_section"
|
|
||||||
@dragover.prevent.stop="onDragover"
|
|
||||||
@drop.prevent.stop="onDrop"
|
|
||||||
>
|
|
||||||
<div class="_content mk-messaging-room">
|
|
||||||
<MkSpacer :content-max="800">
|
|
||||||
<div class="body">
|
|
||||||
<MkPagination v-if="pagination" ref="pagingComponent" :key="userAcct || groupId" :pagination="pagination">
|
|
||||||
<template #empty>
|
|
||||||
<div class="_fullinfo">
|
|
||||||
<img src="/static-assets/badges/info.png" class="_ghost" alt="Info"/>
|
|
||||||
<div>{{ i18n.ts.noMessagesYet }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #default="{ items: messages, fetching: pFetching }">
|
|
||||||
<XList
|
|
||||||
v-if="messages.length > 0"
|
|
||||||
v-slot="{ item: message }"
|
|
||||||
:class="{ messages: true, 'deny-move-transition': pFetching }"
|
|
||||||
:items="messages"
|
|
||||||
direction="up"
|
|
||||||
reversed
|
|
||||||
>
|
|
||||||
<XMessage :key="message.id" :message="message" :is-group="group != null"/>
|
|
||||||
</XList>
|
|
||||||
</template>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
<div v-if="typers.length > 0" class="typers">
|
|
||||||
<I18n :src="i18n.ts.typingUsers" text-tag="span" class="users">
|
|
||||||
<template #users>
|
|
||||||
<b v-for="typer in typers" :key="typer.id" class="user">{{ typer.username }}</b>
|
|
||||||
</template>
|
|
||||||
</I18n>
|
|
||||||
<MkEllipsis/>
|
|
||||||
</div>
|
|
||||||
<transition :name="animation ? 'fade' : ''">
|
|
||||||
<div v-show="showIndicator" class="new-message">
|
|
||||||
<button class="_buttonPrimary" @click="onIndicatorClick"><i class="fas ph-fw ph-lg ph-arrow-circle-down-bold ph-lg"></i>{{ i18n.ts.newMessageExists }}</button>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
<XForm v-if="!fetching" ref="formEl" :user="user" :group="group" class="form"/>
|
|
||||||
</footer>
|
|
||||||
</MkSpacer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, watch, onMounted, nextTick, onBeforeUnmount } from 'vue';
|
|
||||||
import * as Misskey from 'calckey-js';
|
|
||||||
import * as Acct from 'calckey-js/built/acct';
|
|
||||||
import XMessage from './messaging-room.message.vue';
|
|
||||||
import XForm from './messaging-room.form.vue';
|
|
||||||
import XList from '@/components/MkDateSeparatedList.vue';
|
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
|
||||||
import { isBottomVisible, onScrollBottom, scrollToBottom } from '@/scripts/scroll';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { stream } from '@/stream';
|
|
||||||
import * as sound from '@/scripts/sound';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { $i } from '@/account';
|
|
||||||
import { defaultStore } from '@/store';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
userAcct?: string;
|
|
||||||
groupId?: string;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let rootEl = $ref<HTMLDivElement>();
|
|
||||||
let formEl = $ref<InstanceType<typeof XForm>>();
|
|
||||||
let pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
|
||||||
|
|
||||||
let fetching = $ref(true);
|
|
||||||
let user: Misskey.entities.UserDetailed | null = $ref(null);
|
|
||||||
let group: Misskey.entities.UserGroup | null = $ref(null);
|
|
||||||
let typers: Misskey.entities.User[] = $ref([]);
|
|
||||||
let connection: Misskey.ChannelConnection<Misskey.Channels['messaging']> | null = $ref(null);
|
|
||||||
let showIndicator = $ref(false);
|
|
||||||
const {
|
|
||||||
animation,
|
|
||||||
} = defaultStore.reactiveState;
|
|
||||||
|
|
||||||
let pagination: Paging | null = $ref(null);
|
|
||||||
|
|
||||||
watch([() => props.userAcct, () => props.groupId], () => {
|
|
||||||
if (connection) connection.dispose();
|
|
||||||
fetch();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function fetch() {
|
|
||||||
fetching = true;
|
|
||||||
|
|
||||||
if (props.userAcct) {
|
|
||||||
const acct = Acct.parse(props.userAcct);
|
|
||||||
user = await os.api('users/show', { username: acct.username, host: acct.host || undefined });
|
|
||||||
group = null;
|
|
||||||
|
|
||||||
pagination = {
|
|
||||||
endpoint: 'messaging/messages',
|
|
||||||
limit: 20,
|
|
||||||
params: {
|
|
||||||
userId: user.id,
|
|
||||||
},
|
|
||||||
reversed: true,
|
|
||||||
pageEl: $$(rootEl).value,
|
|
||||||
};
|
|
||||||
connection = stream.useChannel('messaging', {
|
|
||||||
otherparty: user.id,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
user = null;
|
|
||||||
group = await os.api('users/groups/show', { groupId: props.groupId });
|
|
||||||
|
|
||||||
pagination = {
|
|
||||||
endpoint: 'messaging/messages',
|
|
||||||
limit: 20,
|
|
||||||
params: {
|
|
||||||
groupId: group?.id,
|
|
||||||
},
|
|
||||||
reversed: true,
|
|
||||||
pageEl: $$(rootEl).value,
|
|
||||||
};
|
|
||||||
connection = stream.useChannel('messaging', {
|
|
||||||
group: group?.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.on('message', onMessage);
|
|
||||||
connection.on('read', onRead);
|
|
||||||
connection.on('deleted', onDeleted);
|
|
||||||
connection.on('typers', _typers => {
|
|
||||||
typers = _typers.filter(u => u.id !== $i?.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', onVisibilitychange);
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
// thisScrollToBottom();
|
|
||||||
window.setTimeout(() => {
|
|
||||||
fetching = false;
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDragover(ev: DragEvent) {
|
|
||||||
if (!ev.dataTransfer) return;
|
|
||||||
|
|
||||||
const isFile = ev.dataTransfer.items[0].kind === 'file';
|
|
||||||
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
|
||||||
|
|
||||||
if (isFile || isDriveFile) {
|
|
||||||
ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
|
|
||||||
} else {
|
|
||||||
ev.dataTransfer.dropEffect = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDrop(ev: DragEvent): void {
|
|
||||||
if (!ev.dataTransfer) return;
|
|
||||||
|
|
||||||
// ファイルだったら
|
|
||||||
if (ev.dataTransfer.files.length === 1) {
|
|
||||||
formEl.upload(ev.dataTransfer.files[0]);
|
|
||||||
return;
|
|
||||||
} else if (ev.dataTransfer.files.length > 1) {
|
|
||||||
os.alert({
|
|
||||||
type: 'error',
|
|
||||||
text: i18n.ts.onlyOneFileCanBeAttached,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#region ドライブのファイル
|
|
||||||
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
|
||||||
if (driveFile != null && driveFile !== '') {
|
|
||||||
const file = JSON.parse(driveFile);
|
|
||||||
formEl.file = file;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMessage(message) {
|
|
||||||
sound.play('chat');
|
|
||||||
|
|
||||||
const _isBottom = isBottomVisible(rootEl, 64);
|
|
||||||
|
|
||||||
pagingComponent.prepend(message);
|
|
||||||
if (message.userId !== $i?.id && !document.hidden) {
|
|
||||||
connection?.send('read', {
|
|
||||||
id: message.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isBottom) {
|
|
||||||
// Scroll to bottom
|
|
||||||
nextTick(() => {
|
|
||||||
thisScrollToBottom();
|
|
||||||
});
|
|
||||||
} else if (message.userId !== $i?.id) {
|
|
||||||
// Notify
|
|
||||||
notifyNewMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRead(x) {
|
|
||||||
if (user) {
|
|
||||||
if (!Array.isArray(x)) x = [x];
|
|
||||||
for (const id of x) {
|
|
||||||
if (pagingComponent.items.some(y => y.id === id)) {
|
|
||||||
const exist = pagingComponent.items.map(y => y.id).indexOf(id);
|
|
||||||
pagingComponent.items[exist] = {
|
|
||||||
...pagingComponent.items[exist],
|
|
||||||
isRead: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (group) {
|
|
||||||
for (const id of x.ids) {
|
|
||||||
if (pagingComponent.items.some(y => y.id === id)) {
|
|
||||||
const exist = pagingComponent.items.map(y => y.id).indexOf(id);
|
|
||||||
pagingComponent.items[exist] = {
|
|
||||||
...pagingComponent.items[exist],
|
|
||||||
reads: [...pagingComponent.items[exist].reads, x.userId],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDeleted(id) {
|
|
||||||
const msg = pagingComponent.items.find(m => m.id === id);
|
|
||||||
if (msg) {
|
|
||||||
pagingComponent.items = pagingComponent.items.filter(m => m.id !== msg.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function thisScrollToBottom() {
|
|
||||||
if (window.location.href.includes('my/messaging/')) {
|
|
||||||
scrollToBottom($$(rootEl).value, { behavior: 'smooth' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onIndicatorClick() {
|
|
||||||
showIndicator = false;
|
|
||||||
thisScrollToBottom();
|
|
||||||
}
|
|
||||||
|
|
||||||
let scrollRemove: (() => void) | null = $ref(null);
|
|
||||||
|
|
||||||
function notifyNewMessage() {
|
|
||||||
showIndicator = true;
|
|
||||||
|
|
||||||
scrollRemove = onScrollBottom(rootEl, () => {
|
|
||||||
showIndicator = false;
|
|
||||||
scrollRemove = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onVisibilitychange() {
|
|
||||||
if (document.hidden) return;
|
|
||||||
for (const message of pagingComponent.items) {
|
|
||||||
if (message.userId !== $i?.id && !message.isRead) {
|
|
||||||
connection?.send('read', {
|
|
||||||
id: message.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
fetch();
|
|
||||||
definePageMetadata(computed(() => ({
|
|
||||||
title: group != null ? group.name : user?.name,
|
|
||||||
icon: 'ph-chats-teardrop-bold ph-lg',
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
connection?.dispose();
|
|
||||||
document.removeEventListener('visibilitychange', onVisibilitychange);
|
|
||||||
if (scrollRemove) scrollRemove();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
XMessage:last-of-type {
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mk-messaging-room {
|
|
||||||
position: relative;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
> .body {
|
|
||||||
.more {
|
|
||||||
display: block;
|
|
||||||
margin: 16px auto;
|
|
||||||
padding: 0 12px;
|
|
||||||
line-height: 24px;
|
|
||||||
color: #fff;
|
|
||||||
background: rgba(#000, 0.3);
|
|
||||||
border-radius: 12px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(#000, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: rgba(#000, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fetching {
|
|
||||||
cursor: wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
> i {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages {
|
|
||||||
padding: 8px 0;
|
|
||||||
|
|
||||||
> ::v-deep(*) {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> footer {
|
|
||||||
width: 100%;
|
|
||||||
position: sticky;
|
|
||||||
z-index: 2;
|
|
||||||
bottom: 0;
|
|
||||||
padding-top: 8px;
|
|
||||||
bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
|
|
||||||
|
|
||||||
> .new-message {
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
> button {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 12px;
|
|
||||||
line-height: 32px;
|
|
||||||
font-size: 12px;
|
|
||||||
border-radius: 16px;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .typers {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 100%;
|
|
||||||
padding: 0 8px 0 8px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: var(--fgTransparentWeak);
|
|
||||||
|
|
||||||
> .users {
|
|
||||||
> .user + .user:before {
|
|
||||||
content: ", ";
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .user:last-of-type:after {
|
|
||||||
content: " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .form {
|
|
||||||
max-height: 12em;
|
|
||||||
overflow-y: scroll;
|
|
||||||
border-top: solid 0.5px var(--divider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active, .fade-leave-active {
|
|
||||||
transition: opacity 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from, .fade-leave-to {
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -542,22 +542,6 @@ export const routes = [
|
||||||
component: page(() => import("./pages/favorites.vue")),
|
component: page(() => import("./pages/favorites.vue")),
|
||||||
loginRequired: true,
|
loginRequired: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "messaging",
|
|
||||||
path: "/my/messaging",
|
|
||||||
component: page(() => import("./pages/messaging/index.vue")),
|
|
||||||
loginRequired: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/my/messaging/:userAcct",
|
|
||||||
component: page(() => import("./pages/messaging/messaging-room.vue")),
|
|
||||||
loginRequired: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/my/messaging/group/:groupId",
|
|
||||||
component: page(() => import("./pages/messaging/messaging-room.vue")),
|
|
||||||
loginRequired: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/my/drive/folder/:folder",
|
path: "/my/drive/folder/:folder",
|
||||||
component: page(() => import("./pages/drive.vue")),
|
component: page(() => import("./pages/drive.vue")),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { defineAsyncComponent, Ref, inject } from "vue";
|
import { defineAsyncComponent, Ref } from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import { pleaseLogin } from "./please-login";
|
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
|
|
|
@ -232,15 +232,6 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
||||||
os.post({ specified: user });
|
os.post({ specified: user });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meId !== user.id
|
|
||||||
? {
|
|
||||||
type: "link",
|
|
||||||
icon: "ph-chats-teardrop-bold ph-lg",
|
|
||||||
text: i18n.ts.startMessaging,
|
|
||||||
to: `/my/messaging/${Acct.toString(user)}`,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
null,
|
|
||||||
{
|
{
|
||||||
icon: "ph-list-bullets-bold ph-lg",
|
icon: "ph-list-bullets-bold ph-lg",
|
||||||
text: i18n.ts.addToList,
|
text: i18n.ts.addToList,
|
||||||
|
|
|
@ -86,7 +86,6 @@ export const defaultStore = markRaw(
|
||||||
"notifications",
|
"notifications",
|
||||||
undefined,
|
undefined,
|
||||||
"followRequests",
|
"followRequests",
|
||||||
"messaging",
|
|
||||||
"explore",
|
"explore",
|
||||||
"favorites",
|
"favorites",
|
||||||
"channels",
|
"channels",
|
||||||
|
|
|
@ -305,33 +305,6 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case "unreadMessagingMessage":
|
|
||||||
if (data.body.groupId === null) {
|
|
||||||
return [
|
|
||||||
t("_notification.youGotMessagingMessageFromUser", {
|
|
||||||
name: getUserName(data.body.user),
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
icon: data.body.user.avatarUrl,
|
|
||||||
badge: iconUrl("comments"),
|
|
||||||
tag: `messaging:user:${data.body.userId}`,
|
|
||||||
data,
|
|
||||||
renotify: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
t("_notification.youGotMessagingMessageFromGroup", {
|
|
||||||
name: data.body.group.name,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
icon: data.body.user.avatarUrl,
|
|
||||||
badge: iconUrl("comments"),
|
|
||||||
tag: `messaging:group:${data.body.groupId}`,
|
|
||||||
data,
|
|
||||||
renotify: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ declare var self: ServiceWorkerGlobalScope;
|
||||||
|
|
||||||
import * as Misskey from "calckey-js";
|
import * as Misskey from "calckey-js";
|
||||||
import { SwMessage, swMessageOrderType } from "@/types";
|
import { SwMessage, swMessageOrderType } from "@/types";
|
||||||
import { acct as getAcct } from "@/filters/user";
|
|
||||||
import { getAccountFromId } from "@/scripts/get-account-from-id";
|
import { getAccountFromId } from "@/scripts/get-account-from-id";
|
||||||
import { getUrlWithLoginId } from "@/scripts/login-id";
|
import { getUrlWithLoginId } from "@/scripts/login-id";
|
||||||
|
|
||||||
|
@ -36,18 +35,6 @@ export function openNote(noteId: string, loginId: string) {
|
||||||
return openClient("push", `/notes/${noteId}`, loginId, { noteId });
|
return openClient("push", `/notes/${noteId}`, loginId, { noteId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openChat(body: any, loginId: string) {
|
|
||||||
if (body.groupId === null) {
|
|
||||||
return openClient("push", `/my/messaging/${getAcct(body.user)}`, loginId, {
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return openClient("push", `/my/messaging/group/${body.groupId}`, loginId, {
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// post-formのオプションから投稿フォームを開く
|
// post-formのオプションから投稿フォームを開く
|
||||||
export async function openPost(options: any, loginId: string) {
|
export async function openPost(options: any, loginId: string) {
|
||||||
// クエリを作成しておく
|
// クエリを作成しておく
|
||||||
|
|
|
@ -2,7 +2,6 @@ declare var self: ServiceWorkerGlobalScope;
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createEmptyNotification,
|
createEmptyNotification,
|
||||||
createNotification,
|
|
||||||
} from "@/scripts/create-notification";
|
} from "@/scripts/create-notification";
|
||||||
import { swLang } from "@/scripts/lang";
|
import { swLang } from "@/scripts/lang";
|
||||||
import { swNotificationRead } from "@/scripts/notification-read";
|
import { swNotificationRead } from "@/scripts/notification-read";
|
||||||
|
@ -65,25 +64,11 @@ self.addEventListener("push", (ev) => {
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
// case 'driveFileCreated':
|
// case 'driveFileCreated':
|
||||||
case "notification":
|
case "notification":
|
||||||
case "unreadMessagingMessage":
|
|
||||||
// 1日以上経過している場合は無視
|
|
||||||
if (new Date().getTime() - data.dateTime > 1000 * 60 * 60 * 24)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// クライアントがあったらストリームに接続しているということなので通知しない
|
|
||||||
if (clients.length !== 0) break;
|
|
||||||
|
|
||||||
return createNotification(data);
|
|
||||||
case "readAllNotifications":
|
case "readAllNotifications":
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await self.registration.getNotifications()) {
|
||||||
if (n?.data?.type === "notification") n.close();
|
if (n?.data?.type === "notification") n.close();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "readAllMessagingMessages":
|
|
||||||
for (const n of await self.registration.getNotifications()) {
|
|
||||||
if (n?.data?.type === "unreadMessagingMessage") n.close();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "readNotifications":
|
case "readNotifications":
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await self.registration.getNotifications()) {
|
||||||
if (data.body?.notificationIds?.includes(n.data.body.id)) {
|
if (data.body?.notificationIds?.includes(n.data.body.id)) {
|
||||||
|
@ -91,18 +76,6 @@ self.addEventListener("push", (ev) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "readAllMessagingMessagesOfARoom":
|
|
||||||
for (const n of await self.registration.getNotifications()) {
|
|
||||||
if (
|
|
||||||
n.data.type === "unreadMessagingMessage" &&
|
|
||||||
("userId" in data.body
|
|
||||||
? data.body.userId === n.data.body.userId
|
|
||||||
: data.body.groupId === n.data.body.groupId)
|
|
||||||
) {
|
|
||||||
n.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return createEmptyNotification();
|
return createEmptyNotification();
|
||||||
|
@ -210,9 +183,6 @@ self.addEventListener(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "unreadMessagingMessage":
|
|
||||||
client = await swos.openChat(data.body, id);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client) {
|
if (client) {
|
||||||
|
|
|
@ -13,11 +13,8 @@ export type SwMessage = {
|
||||||
// Defined also @/services/push-notification.ts#L7-L14
|
// Defined also @/services/push-notification.ts#L7-L14
|
||||||
type pushNotificationDataSourceMap = {
|
type pushNotificationDataSourceMap = {
|
||||||
notification: Misskey.entities.Notification;
|
notification: Misskey.entities.Notification;
|
||||||
unreadMessagingMessage: Misskey.entities.MessagingMessage;
|
|
||||||
readNotifications: { notificationIds: string[] };
|
readNotifications: { notificationIds: string[] };
|
||||||
readAllNotifications: undefined;
|
readAllNotifications: undefined;
|
||||||
readAllMessagingMessages: undefined;
|
|
||||||
readAllMessagingMessagesOfARoom: { userId: string } | { groupId: string };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type pushNotificationData<
|
export type pushNotificationData<
|
||||||
|
|
358
pnpm-lock.yaml
358
pnpm-lock.yaml
|
@ -7,8 +7,8 @@ importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@bull-board/api': ^4.10.2
|
'@bull-board/api': ^4.12.2
|
||||||
'@bull-board/ui': ^4.10.2
|
'@bull-board/ui': ^4.12.2
|
||||||
'@tensorflow/tfjs': ^3.21.0
|
'@tensorflow/tfjs': ^3.21.0
|
||||||
'@types/gulp': 4.0.10
|
'@types/gulp': 4.0.10
|
||||||
'@types/gulp-rename': 2.0.1
|
'@types/gulp-rename': 2.0.1
|
||||||
|
@ -29,8 +29,8 @@ importers:
|
||||||
start-server-and-test: 1.15.2
|
start-server-and-test: 1.15.2
|
||||||
typescript: 4.9.4
|
typescript: 4.9.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@bull-board/api': 4.10.2
|
'@bull-board/api': 4.12.2
|
||||||
'@bull-board/ui': 4.10.2
|
'@bull-board/ui': 4.12.2
|
||||||
'@tensorflow/tfjs': 3.21.0_seedrandom@3.0.5
|
'@tensorflow/tfjs': 3.21.0_seedrandom@3.0.5
|
||||||
calckey-js: 0.0.20
|
calckey-js: 0.0.20
|
||||||
js-yaml: 4.1.0
|
js-yaml: 4.1.0
|
||||||
|
@ -672,6 +672,12 @@ packages:
|
||||||
redis-info: 3.1.0
|
redis-info: 3.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@bull-board/api/4.12.2:
|
||||||
|
resolution: {integrity: sha512-efF8K1pvfEEft2ELQwCBe/a225vHufCjM2hfcyvmIG/SUX6TlKQFUFZ1+KV0wenD9wxvUVb5wxUu6on+HrZiVg==}
|
||||||
|
dependencies:
|
||||||
|
redis-info: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@bull-board/koa/4.10.2_6tybghmia4wsnt33xeid7y4rby:
|
/@bull-board/koa/4.10.2_6tybghmia4wsnt33xeid7y4rby:
|
||||||
resolution: {integrity: sha512-gabPtsMOt2SQHkS5VcY1q/FCpbBRFiFrbWbcouZ7zWKg413J8nG+yErz3pc0rbmp23kbKX6wTG/diWKhE7EWbA==}
|
resolution: {integrity: sha512-gabPtsMOt2SQHkS5VcY1q/FCpbBRFiFrbWbcouZ7zWKg413J8nG+yErz3pc0rbmp23kbKX6wTG/diWKhE7EWbA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -746,6 +752,12 @@ packages:
|
||||||
'@bull-board/api': 4.10.2
|
'@bull-board/api': 4.10.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@bull-board/ui/4.12.2:
|
||||||
|
resolution: {integrity: sha512-jB/OOEhg+DUL6ssmtQYTK+iYd3iy68bbozOp+q2xVUC4V7zeFmYF25sIApYFTNfbjuUMesAVOiX4u0gNEo/J7w==}
|
||||||
|
dependencies:
|
||||||
|
'@bull-board/api': 4.12.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@colors/colors/1.5.0:
|
/@colors/colors/1.5.0:
|
||||||
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
||||||
engines: {node: '>=0.1.90'}
|
engines: {node: '>=0.1.90'}
|
||||||
|
@ -1191,6 +1203,15 @@ packages:
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
'@jridgewell/trace-mapping': 0.3.17
|
'@jridgewell/trace-mapping': 0.3.17
|
||||||
|
|
||||||
|
/@jridgewell/gen-mapping/0.3.3:
|
||||||
|
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/set-array': 1.1.2
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
|
'@jridgewell/trace-mapping': 0.3.18
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@jridgewell/resolve-uri/3.1.0:
|
/@jridgewell/resolve-uri/3.1.0:
|
||||||
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
|
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
|
@ -1206,15 +1227,33 @@ packages:
|
||||||
'@jridgewell/trace-mapping': 0.3.17
|
'@jridgewell/trace-mapping': 0.3.17
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/source-map/0.3.3:
|
||||||
|
resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/gen-mapping': 0.3.3
|
||||||
|
'@jridgewell/trace-mapping': 0.3.18
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@jridgewell/sourcemap-codec/1.4.14:
|
/@jridgewell/sourcemap-codec/1.4.14:
|
||||||
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
||||||
|
|
||||||
|
/@jridgewell/sourcemap-codec/1.4.15:
|
||||||
|
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@jridgewell/trace-mapping/0.3.17:
|
/@jridgewell/trace-mapping/0.3.17:
|
||||||
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/resolve-uri': 3.1.0
|
'@jridgewell/resolve-uri': 3.1.0
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
|
||||||
|
/@jridgewell/trace-mapping/0.3.18:
|
||||||
|
resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/resolve-uri': 3.1.0
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@jridgewell/trace-mapping/0.3.9:
|
/@jridgewell/trace-mapping/0.3.9:
|
||||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1865,7 +1904,7 @@ packages:
|
||||||
'@types/webgl-ext': 0.0.30
|
'@types/webgl-ext': 0.0.30
|
||||||
'@webgpu/types': 0.1.16
|
'@webgpu/types': 0.1.16
|
||||||
long: 4.0.0
|
long: 4.0.0
|
||||||
node-fetch: 2.6.8
|
node-fetch: 2.6.9
|
||||||
seedrandom: 3.0.5
|
seedrandom: 3.0.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
|
@ -1894,8 +1933,8 @@ packages:
|
||||||
seedrandom: ^3.0.5
|
seedrandom: ^3.0.5
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tensorflow/tfjs-core': 3.21.0
|
'@tensorflow/tfjs-core': 3.21.0
|
||||||
'@types/node-fetch': 2.6.2
|
'@types/node-fetch': 2.6.3
|
||||||
node-fetch: 2.6.8
|
node-fetch: 2.6.9
|
||||||
seedrandom: 3.0.5
|
seedrandom: 3.0.5
|
||||||
string_decoder: 1.3.0
|
string_decoder: 1.3.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -1965,7 +2004,7 @@ packages:
|
||||||
'@tensorflow/tfjs-layers': 3.21.0_aipmo6igpprgzt4umpaa3m6sn4
|
'@tensorflow/tfjs-layers': 3.21.0_aipmo6igpprgzt4umpaa3m6sn4
|
||||||
argparse: 1.0.10
|
argparse: 1.0.10
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
core-js: 3.27.1
|
core-js: 3.30.1
|
||||||
regenerator-runtime: 0.13.11
|
regenerator-runtime: 0.13.11
|
||||||
yargs: 16.2.0
|
yargs: 16.2.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -2138,11 +2177,18 @@ packages:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.11.18
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/glob-stream/6.1.1:
|
/@types/glob-stream/6.1.2:
|
||||||
resolution: {integrity: sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==}
|
resolution: {integrity: sha512-EIJSLP/nGyMzD8aFhJljO9nv3EmQ10xk1+dl+i15AITrcWLhhTTPmNMFK0TWcGRvVYuSlA1VPi1fe8tbgDsUhg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/glob': 8.0.0
|
'@types/glob': 7.2.0
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.15.12
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/glob/7.2.0:
|
||||||
|
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/minimatch': 5.1.2
|
||||||
|
'@types/node': 18.15.12
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/glob/8.0.0:
|
/@types/glob/8.0.0:
|
||||||
|
@ -2152,10 +2198,17 @@ packages:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.11.18
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/glob/8.1.0:
|
||||||
|
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
|
||||||
|
dependencies:
|
||||||
|
'@types/minimatch': 5.1.2
|
||||||
|
'@types/node': 18.15.12
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/gulp-rename/2.0.1:
|
/@types/gulp-rename/2.0.1:
|
||||||
resolution: {integrity: sha512-9ZjeS2RHEnmBmTcyi2+oeye3BgCsWhvi4uv3qCnAg8i6plOuRdaeNxjOves0ELysEXYLBl7bCl5fbVs7AZtgTA==}
|
resolution: {integrity: sha512-9ZjeS2RHEnmBmTcyi2+oeye3BgCsWhvi4uv3qCnAg8i6plOuRdaeNxjOves0ELysEXYLBl7bCl5fbVs7AZtgTA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.15.12
|
||||||
'@types/vinyl': 2.0.7
|
'@types/vinyl': 2.0.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -2163,7 +2216,7 @@ packages:
|
||||||
resolution: {integrity: sha512-spgZHJFqiEJGwqGlf7T/k4nkBpBcLgP7T0EfN6G2vvnhUfvd4uO1h8RwpXOE8x/54DVYUs1XCAtBHkX/R3axAQ==}
|
resolution: {integrity: sha512-spgZHJFqiEJGwqGlf7T/k4nkBpBcLgP7T0EfN6G2vvnhUfvd4uO1h8RwpXOE8x/54DVYUs1XCAtBHkX/R3axAQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/undertaker': 1.2.8
|
'@types/undertaker': 1.2.8
|
||||||
'@types/vinyl-fs': 2.4.12
|
'@types/vinyl-fs': 3.0.1
|
||||||
chokidar: 3.5.3
|
chokidar: 3.5.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -2382,6 +2435,13 @@ packages:
|
||||||
form-data: 3.0.1
|
form-data: 3.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/node-fetch/2.6.3:
|
||||||
|
resolution: {integrity: sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 18.15.12
|
||||||
|
form-data: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/node-fetch/3.0.3:
|
/@types/node-fetch/3.0.3:
|
||||||
resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==}
|
resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==}
|
||||||
deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.
|
deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.
|
||||||
|
@ -2391,10 +2451,18 @@ packages:
|
||||||
|
|
||||||
/@types/node/14.18.36:
|
/@types/node/14.18.36:
|
||||||
resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==}
|
resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@types/node/14.18.42:
|
||||||
|
resolution: {integrity: sha512-xefu+RBie4xWlK8hwAzGh3npDz/4VhF6icY/shU+zv/1fNn+ZVG7T7CRwe9LId9sAYRPxI+59QBPuKL3WpyGRg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node/18.11.18:
|
/@types/node/18.11.18:
|
||||||
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
|
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
|
||||||
|
|
||||||
|
/@types/node/18.15.12:
|
||||||
|
resolution: {integrity: sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg==}
|
||||||
|
|
||||||
/@types/nodemailer/6.4.7:
|
/@types/nodemailer/6.4.7:
|
||||||
resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==}
|
resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2464,6 +2532,13 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.11.18
|
||||||
|
|
||||||
|
/@types/rimraf/2.0.5:
|
||||||
|
resolution: {integrity: sha512-YyP+VfeaqAyFmXoTh3HChxOQMyjByRMsHU7kc5KOJkSlXudhMhQIALbYV7rHh/l8d2lX3VUQzprrcAgWdRuU8g==}
|
||||||
|
dependencies:
|
||||||
|
'@types/glob': 8.1.0
|
||||||
|
'@types/node': 18.15.12
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/sanitize-html/2.8.0:
|
/@types/sanitize-html/2.8.0:
|
||||||
resolution: {integrity: sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==}
|
resolution: {integrity: sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2539,7 +2614,7 @@ packages:
|
||||||
/@types/undertaker/1.2.8:
|
/@types/undertaker/1.2.8:
|
||||||
resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==}
|
resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.15.12
|
||||||
'@types/undertaker-registry': 1.0.1
|
'@types/undertaker-registry': 1.0.1
|
||||||
async-done: 1.3.2
|
async-done: 1.3.2
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2548,11 +2623,12 @@ packages:
|
||||||
resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
|
resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/vinyl-fs/2.4.12:
|
/@types/vinyl-fs/3.0.1:
|
||||||
resolution: {integrity: sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==}
|
resolution: {integrity: sha512-me2Gcxw23pZp62oqPoiTDDMz/txEmtEZzXM/D/VTr+xUX4LiNA+nQPs38SSPu5KHnsaEER4HEtzWU5qJRXigfA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/glob-stream': 6.1.1
|
'@types/glob-stream': 6.1.2
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.15.12
|
||||||
|
'@types/rimraf': 2.0.5
|
||||||
'@types/vinyl': 2.0.7
|
'@types/vinyl': 2.0.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -2560,7 +2636,7 @@ packages:
|
||||||
resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==}
|
resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/expect': 1.20.4
|
'@types/expect': 1.20.4
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.15.12
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/web-push/3.3.2:
|
/@types/web-push/3.3.2:
|
||||||
|
@ -2599,7 +2675,7 @@ packages:
|
||||||
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
|
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 14.18.42
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
@ -2885,6 +2961,12 @@ packages:
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
/acorn/8.8.2:
|
||||||
|
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/adm-zip/0.5.10:
|
/adm-zip/0.5.10:
|
||||||
resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==}
|
resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
|
@ -3288,7 +3370,7 @@ packages:
|
||||||
resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==}
|
resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 1.7.7
|
browserslist: 1.7.7
|
||||||
caniuse-db: 1.0.30001443
|
caniuse-db: 1.0.30001480
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
num2fraction: 1.2.2
|
num2fraction: 1.2.2
|
||||||
postcss: 5.2.18
|
postcss: 5.2.18
|
||||||
|
@ -3343,7 +3425,7 @@ packages:
|
||||||
/axios/0.25.0_debug@4.3.4:
|
/axios/0.25.0_debug@4.3.4:
|
||||||
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.2
|
follow-redirects: 1.15.2_debug@4.3.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -3600,8 +3682,8 @@ packages:
|
||||||
deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.
|
deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-db: 1.0.30001443
|
caniuse-db: 1.0.30001480
|
||||||
electron-to-chromium: 1.4.284
|
electron-to-chromium: 1.4.368
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/browserslist/4.21.4:
|
/browserslist/4.21.4:
|
||||||
|
@ -3826,13 +3908,13 @@ packages:
|
||||||
autobind-decorator: 2.4.0
|
autobind-decorator: 2.4.0
|
||||||
eventemitter3: 4.0.7
|
eventemitter3: 4.0.7
|
||||||
reconnecting-websocket: 4.4.0
|
reconnecting-websocket: 4.4.0
|
||||||
semver: 7.3.8
|
semver: 7.5.0
|
||||||
|
|
||||||
/call-bind/1.0.2:
|
/call-bind/1.0.2:
|
||||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
get-intrinsic: 1.1.3
|
get-intrinsic: 1.2.0
|
||||||
|
|
||||||
/callsites/3.1.0:
|
/callsites/3.1.0:
|
||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
|
@ -3857,13 +3939,13 @@ packages:
|
||||||
resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==}
|
resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 1.7.7
|
browserslist: 1.7.7
|
||||||
caniuse-db: 1.0.30001443
|
caniuse-db: 1.0.30001480
|
||||||
lodash.memoize: 4.1.2
|
lodash.memoize: 4.1.2
|
||||||
lodash.uniq: 4.5.0
|
lodash.uniq: 4.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caniuse-db/1.0.30001443:
|
/caniuse-db/1.0.30001480:
|
||||||
resolution: {integrity: sha512-4KKthVYyooNIOhO1w0OJ13EhEwOGECMrZdkeyDydhvYXaTDA3WdhR8amoJnAgpSgcCR26aOAWk6N9ANVYlv2oQ==}
|
resolution: {integrity: sha512-DVj/w8brn4D//RjICBR23GP/4y28fk2Bsb3PDN1orV3/7rEB8czfr0TxYBY0weRPwUsj6n9+kMoPxUD7wyvcJg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caniuse-lite/1.0.30001443:
|
/caniuse-lite/1.0.30001443:
|
||||||
|
@ -4045,8 +4127,8 @@ packages:
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/ci-info/3.7.1:
|
/ci-info/3.8.0:
|
||||||
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
|
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -4176,7 +4258,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
process-nextick-args: 2.0.1
|
process-nextick-args: 2.0.1
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/cluster-key-slot/1.1.1:
|
/cluster-key-slot/1.1.1:
|
||||||
|
@ -4294,8 +4376,8 @@ packages:
|
||||||
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
|
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/colorette/2.0.19:
|
/colorette/2.0.20:
|
||||||
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
|
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/colormin/1.1.2:
|
/colormin/1.1.2:
|
||||||
|
@ -4372,7 +4454,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer-from: 1.1.2
|
buffer-from: 1.1.2
|
||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
typedarray: 0.0.6
|
typedarray: 0.0.6
|
||||||
|
|
||||||
/condense-newlines/0.2.1:
|
/condense-newlines/0.2.1:
|
||||||
|
@ -4780,6 +4862,11 @@ packages:
|
||||||
resolution: {integrity: sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==}
|
resolution: {integrity: sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
|
||||||
|
/core-js/3.30.1:
|
||||||
|
resolution: {integrity: sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==}
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/core-util-is/1.0.2:
|
/core-util-is/1.0.2:
|
||||||
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
|
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
|
||||||
|
|
||||||
|
@ -4960,7 +5047,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@cypress/request': 2.88.11
|
'@cypress/request': 2.88.11
|
||||||
'@cypress/xvfb': 1.2.4_supports-color@8.1.1
|
'@cypress/xvfb': 1.2.4_supports-color@8.1.1
|
||||||
'@types/node': 14.18.36
|
'@types/node': 14.18.42
|
||||||
'@types/sinonjs__fake-timers': 8.1.1
|
'@types/sinonjs__fake-timers': 8.1.1
|
||||||
'@types/sizzle': 2.3.3
|
'@types/sizzle': 2.3.3
|
||||||
arch: 2.2.0
|
arch: 2.2.0
|
||||||
|
@ -4990,12 +5077,12 @@ packages:
|
||||||
listr2: 3.14.0_enquirer@2.3.6
|
listr2: 3.14.0_enquirer@2.3.6
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
log-symbols: 4.1.0
|
log-symbols: 4.1.0
|
||||||
minimist: 1.2.7
|
minimist: 1.2.8
|
||||||
ospath: 1.2.2
|
ospath: 1.2.2
|
||||||
pretty-bytes: 5.6.0
|
pretty-bytes: 5.6.0
|
||||||
proxy-from-env: 1.0.0
|
proxy-from-env: 1.0.0
|
||||||
request-progress: 3.0.0
|
request-progress: 3.0.0
|
||||||
semver: 7.3.8
|
semver: 7.5.0
|
||||||
supports-color: 8.1.1
|
supports-color: 8.1.1
|
||||||
tmp: 0.2.1
|
tmp: 0.2.1
|
||||||
untildify: 4.0.0
|
untildify: 4.0.0
|
||||||
|
@ -5241,8 +5328,8 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/define-properties/1.1.4:
|
/define-properties/1.2.0:
|
||||||
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
|
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
has-property-descriptors: 1.0.0
|
has-property-descriptors: 1.0.0
|
||||||
|
@ -5460,7 +5547,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
end-of-stream: 1.4.4
|
end-of-stream: 1.4.4
|
||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
stream-shift: 1.0.1
|
stream-shift: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -5508,6 +5595,10 @@ packages:
|
||||||
resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
|
resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/electron-to-chromium/1.4.368:
|
||||||
|
resolution: {integrity: sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/emoji-regex/8.0.0:
|
/emoji-regex/8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
|
@ -6298,7 +6389,7 @@ packages:
|
||||||
resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
|
resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/follow-redirects/1.15.2:
|
/follow-redirects/1.15.2:
|
||||||
|
@ -6309,6 +6400,19 @@ packages:
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/follow-redirects/1.15.2_debug@4.3.4:
|
||||||
|
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
peerDependencies:
|
||||||
|
debug: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
debug:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/for-each/0.3.3:
|
/for-each/0.3.3:
|
||||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||||
|
@ -6416,7 +6520,7 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
at-least-node: 1.0.0
|
at-least-node: 1.0.0
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
jsonfile: 6.1.0
|
jsonfile: 6.1.0
|
||||||
universalify: 2.0.0
|
universalify: 2.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -6439,7 +6543,7 @@ packages:
|
||||||
resolution: {integrity: sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==}
|
resolution: {integrity: sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
through2: 2.0.5
|
through2: 2.0.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -6520,6 +6624,14 @@ packages:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/get-intrinsic/1.2.0:
|
||||||
|
resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==}
|
||||||
|
dependencies:
|
||||||
|
function-bind: 1.1.1
|
||||||
|
has: 1.0.3
|
||||||
|
has-symbols: 1.0.3
|
||||||
|
|
||||||
/get-paths/0.0.7:
|
/get-paths/0.0.7:
|
||||||
resolution: {integrity: sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA==}
|
resolution: {integrity: sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA==}
|
||||||
|
@ -6641,7 +6753,7 @@ packages:
|
||||||
is-negated-glob: 1.0.0
|
is-negated-glob: 1.0.0
|
||||||
ordered-read-streams: 1.0.1
|
ordered-read-streams: 1.0.1
|
||||||
pumpify: 1.5.1
|
pumpify: 1.5.1
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
remove-trailing-separator: 1.1.0
|
remove-trailing-separator: 1.1.0
|
||||||
to-absolute-glob: 2.0.2
|
to-absolute-glob: 2.0.2
|
||||||
unique-stream: 2.3.1
|
unique-stream: 2.3.1
|
||||||
|
@ -6827,6 +6939,10 @@ packages:
|
||||||
/graceful-fs/4.2.10:
|
/graceful-fs/4.2.10:
|
||||||
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
||||||
|
|
||||||
|
/graceful-fs/4.2.11:
|
||||||
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/grapheme-splitter/1.0.4:
|
/grapheme-splitter/1.0.4:
|
||||||
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
||||||
|
|
||||||
|
@ -6876,7 +6992,7 @@ packages:
|
||||||
resolution: {integrity: sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==}
|
resolution: {integrity: sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.11.18
|
'@types/node': 18.15.12
|
||||||
'@types/vinyl': 2.0.7
|
'@types/vinyl': 2.0.7
|
||||||
istextorbinary: 3.3.0
|
istextorbinary: 3.3.0
|
||||||
replacestream: 4.0.3
|
replacestream: 4.0.3
|
||||||
|
@ -6888,7 +7004,7 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
plugin-error: 1.0.1
|
plugin-error: 1.0.1
|
||||||
terser: 5.16.1
|
terser: 5.17.1
|
||||||
through2: 4.0.2
|
through2: 4.0.2
|
||||||
vinyl-sourcemaps-apply: 0.2.1
|
vinyl-sourcemaps-apply: 0.2.1
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -6956,7 +7072,7 @@ packages:
|
||||||
/has-property-descriptors/1.0.0:
|
/has-property-descriptors/1.0.0:
|
||||||
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.1.3
|
get-intrinsic: 1.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/has-symbol-support-x/1.4.2:
|
/has-symbol-support-x/1.4.2:
|
||||||
|
@ -7464,7 +7580,7 @@ packages:
|
||||||
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
|
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
ci-info: 3.7.1
|
ci-info: 3.8.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/is-core-module/2.11.0:
|
/is-core-module/2.11.0:
|
||||||
|
@ -7472,6 +7588,12 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
|
|
||||||
|
/is-core-module/2.12.0:
|
||||||
|
resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==}
|
||||||
|
dependencies:
|
||||||
|
has: 1.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/is-data-descriptor/0.1.4:
|
/is-data-descriptor/0.1.4:
|
||||||
resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==}
|
resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -7802,8 +7924,8 @@ packages:
|
||||||
engines: {node: '>= 0.6.0'}
|
engines: {node: '>= 0.6.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/joi/17.7.0:
|
/joi/17.9.1:
|
||||||
resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==}
|
resolution: {integrity: sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hapi/hoek': 9.3.0
|
'@hapi/hoek': 9.3.0
|
||||||
'@hapi/topo': 5.1.0
|
'@hapi/topo': 5.1.0
|
||||||
|
@ -7985,7 +8107,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
universalify: 2.0.0
|
universalify: 2.0.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/jsonld/6.0.0:
|
/jsonld/6.0.0:
|
||||||
|
@ -8416,7 +8538,7 @@ packages:
|
||||||
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
|
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
|
||||||
engines: {node: '>= 0.6.3'}
|
engines: {node: '>= 0.6.3'}
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
|
|
||||||
/lcid/1.0.0:
|
/lcid/1.0.0:
|
||||||
resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==}
|
resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==}
|
||||||
|
@ -8458,7 +8580,7 @@ packages:
|
||||||
is-plain-object: 2.0.4
|
is-plain-object: 2.0.4
|
||||||
object.map: 1.0.1
|
object.map: 1.0.1
|
||||||
rechoir: 0.6.2
|
rechoir: 0.6.2
|
||||||
resolve: 1.22.1
|
resolve: 1.22.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -8477,7 +8599,7 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
cli-truncate: 2.1.0
|
cli-truncate: 2.1.0
|
||||||
colorette: 2.0.19
|
colorette: 2.0.20
|
||||||
enquirer: 2.3.6
|
enquirer: 2.3.6
|
||||||
log-update: 4.0.0
|
log-update: 4.0.0
|
||||||
p-map: 4.0.0
|
p-map: 4.0.0
|
||||||
|
@ -8512,7 +8634,7 @@ packages:
|
||||||
resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==}
|
resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
parse-json: 2.2.0
|
parse-json: 2.2.0
|
||||||
pify: 2.3.0
|
pify: 2.3.0
|
||||||
pinkie-promise: 2.0.1
|
pinkie-promise: 2.0.1
|
||||||
|
@ -8780,7 +8902,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
findup-sync: 2.0.0
|
findup-sync: 2.0.0
|
||||||
micromatch: 3.1.10
|
micromatch: 3.1.10
|
||||||
resolve: 1.22.1
|
resolve: 1.22.2
|
||||||
stack-trace: 0.0.10
|
stack-trace: 0.0.10
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -8906,6 +9028,9 @@ packages:
|
||||||
/minimist/1.2.7:
|
/minimist/1.2.7:
|
||||||
resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
|
resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
|
||||||
|
|
||||||
|
/minimist/1.2.8:
|
||||||
|
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||||
|
|
||||||
/minipass-collect/1.0.2:
|
/minipass-collect/1.0.2:
|
||||||
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
|
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -9005,7 +9130,7 @@ packages:
|
||||||
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
|
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist: 1.2.7
|
minimist: 1.2.8
|
||||||
|
|
||||||
/mkdirp/1.0.4:
|
/mkdirp/1.0.4:
|
||||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||||
|
@ -9247,6 +9372,18 @@ packages:
|
||||||
whatwg-url: 5.0.0
|
whatwg-url: 5.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/node-fetch/2.6.9:
|
||||||
|
resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: 5.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/node-fetch/3.3.0:
|
/node-fetch/3.3.0:
|
||||||
resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==}
|
resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
@ -9319,7 +9456,7 @@ packages:
|
||||||
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
hosted-git-info: 2.8.9
|
hosted-git-info: 2.8.9
|
||||||
resolve: 1.22.1
|
resolve: 1.22.2
|
||||||
semver: 5.7.1
|
semver: 5.7.1
|
||||||
validate-npm-package-license: 3.0.4
|
validate-npm-package-license: 3.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -9503,7 +9640,7 @@ packages:
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
define-properties: 1.1.4
|
define-properties: 1.2.0
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -9615,7 +9752,7 @@ packages:
|
||||||
/ordered-read-streams/1.0.1:
|
/ordered-read-streams/1.0.1:
|
||||||
resolution: {integrity: sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==}
|
resolution: {integrity: sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/os-filter-obj/2.0.0:
|
/os-filter-obj/2.0.0:
|
||||||
|
@ -9870,7 +10007,7 @@ packages:
|
||||||
resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==}
|
resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
pify: 2.3.0
|
pify: 2.3.0
|
||||||
pinkie-promise: 2.0.1
|
pinkie-promise: 2.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -10539,6 +10676,10 @@ packages:
|
||||||
resolution: {integrity: sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==}
|
resolution: {integrity: sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
/punycode/2.3.0:
|
||||||
|
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
/pureimage/0.3.15:
|
/pureimage/0.3.15:
|
||||||
resolution: {integrity: sha512-QpQYEV8nxVb84en7D0nKXwG0bdmwmlsSg9QnqxpEOExvUXdbmo6Lw/UoxSXD9z+ryvWDkgWqZsIM3iPCAh4dXg==}
|
resolution: {integrity: sha512-QpQYEV8nxVb84en7D0nKXwG0bdmwmlsSg9QnqxpEOExvUXdbmo6Lw/UoxSXD9z+ryvWDkgWqZsIM3iPCAh4dXg==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
@ -10733,6 +10874,17 @@ packages:
|
||||||
string_decoder: 1.1.1
|
string_decoder: 1.1.1
|
||||||
util-deprecate: 1.0.2
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
|
/readable-stream/2.3.8:
|
||||||
|
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
|
||||||
|
dependencies:
|
||||||
|
core-util-is: 1.0.3
|
||||||
|
inherits: 2.0.4
|
||||||
|
isarray: 1.0.0
|
||||||
|
process-nextick-args: 2.0.1
|
||||||
|
safe-buffer: 5.1.2
|
||||||
|
string_decoder: 1.1.1
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
/readable-stream/3.6.0:
|
/readable-stream/3.6.0:
|
||||||
resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
|
resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
@ -10740,6 +10892,16 @@ packages:
|
||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
string_decoder: 1.3.0
|
string_decoder: 1.3.0
|
||||||
util-deprecate: 1.0.2
|
util-deprecate: 1.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/readable-stream/3.6.2:
|
||||||
|
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dependencies:
|
||||||
|
inherits: 2.0.4
|
||||||
|
string_decoder: 1.3.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/readable-web-to-node-stream/3.0.2:
|
/readable-web-to-node-stream/3.0.2:
|
||||||
resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
|
resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
|
||||||
|
@ -10764,7 +10926,7 @@ packages:
|
||||||
resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
|
resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
resolve: 1.22.1
|
resolve: 1.22.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/reconnecting-websocket/4.4.0:
|
/reconnecting-websocket/4.4.0:
|
||||||
|
@ -10893,7 +11055,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp: 1.0.5
|
escape-string-regexp: 1.0.5
|
||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/request-progress/3.0.0:
|
/request-progress/3.0.0:
|
||||||
|
@ -10997,6 +11159,15 @@ packages:
|
||||||
path-parse: 1.0.7
|
path-parse: 1.0.7
|
||||||
supports-preserve-symlinks-flag: 1.0.0
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
|
/resolve/1.22.2:
|
||||||
|
resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
is-core-module: 2.12.0
|
||||||
|
path-parse: 1.0.7
|
||||||
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/responselike/1.0.2:
|
/responselike/1.0.2:
|
||||||
resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==}
|
resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -11106,7 +11277,7 @@ packages:
|
||||||
/rxjs/7.8.0:
|
/rxjs/7.8.0:
|
||||||
resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==}
|
resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.4.1
|
tslib: 2.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/s-age/1.1.2:
|
/s-age/1.1.2:
|
||||||
|
@ -11229,6 +11400,13 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache: 6.0.0
|
lru-cache: 6.0.0
|
||||||
|
|
||||||
|
/semver/7.5.0:
|
||||||
|
resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
lru-cache: 6.0.0
|
||||||
|
|
||||||
/serialize-javascript/6.0.0:
|
/serialize-javascript/6.0.0:
|
||||||
resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
|
resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -11314,7 +11492,7 @@ packages:
|
||||||
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
get-intrinsic: 1.1.3
|
get-intrinsic: 1.2.0
|
||||||
object-inspect: 1.12.3
|
object-inspect: 1.12.3
|
||||||
|
|
||||||
/sigmund/1.0.1:
|
/sigmund/1.0.1:
|
||||||
|
@ -11496,11 +11674,11 @@ packages:
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/spdx-correct/3.1.1:
|
/spdx-correct/3.2.0:
|
||||||
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
spdx-expression-parse: 3.0.1
|
spdx-expression-parse: 3.0.1
|
||||||
spdx-license-ids: 3.0.12
|
spdx-license-ids: 3.0.13
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/spdx-exceptions/2.3.0:
|
/spdx-exceptions/2.3.0:
|
||||||
|
@ -11511,11 +11689,11 @@ packages:
|
||||||
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
|
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
spdx-exceptions: 2.3.0
|
spdx-exceptions: 2.3.0
|
||||||
spdx-license-ids: 3.0.12
|
spdx-license-ids: 3.0.13
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/spdx-license-ids/3.0.12:
|
/spdx-license-ids/3.0.13:
|
||||||
resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==}
|
resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/speakeasy/2.0.0:
|
/speakeasy/2.0.0:
|
||||||
|
@ -11980,6 +12158,17 @@ packages:
|
||||||
source-map-support: 0.5.21
|
source-map-support: 0.5.21
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/terser/5.17.1:
|
||||||
|
resolution: {integrity: sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/source-map': 0.3.3
|
||||||
|
acorn: 8.8.2
|
||||||
|
commander: 2.20.3
|
||||||
|
source-map-support: 0.5.21
|
||||||
|
dev: true
|
||||||
|
|
||||||
/tesseract.js-core/3.0.2:
|
/tesseract.js-core/3.0.2:
|
||||||
resolution: {integrity: sha512-2fD76ka9nO/C616R0fq+M9Zu91DA3vEfyozp0jlxaJOBmpfeprtgRP3cqVweZh2darE1kK/DazoxZ65g7WU99Q==}
|
resolution: {integrity: sha512-2fD76ka9nO/C616R0fq+M9Zu91DA3vEfyozp0jlxaJOBmpfeprtgRP3cqVweZh2darE1kK/DazoxZ65g7WU99Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -12060,14 +12249,14 @@ packages:
|
||||||
/through2/2.0.5:
|
/through2/2.0.5:
|
||||||
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
|
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
xtend: 4.0.2
|
xtend: 4.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/through2/4.0.2:
|
/through2/4.0.2:
|
||||||
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
|
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 3.6.0
|
readable-stream: 3.6.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/time-stamp/1.1.0:
|
/time-stamp/1.1.0:
|
||||||
|
@ -12163,7 +12352,7 @@ packages:
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
psl: 1.9.0
|
psl: 1.9.0
|
||||||
punycode: 2.2.0
|
punycode: 2.3.0
|
||||||
|
|
||||||
/tough-cookie/4.1.2:
|
/tough-cookie/4.1.2:
|
||||||
resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
|
resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
|
||||||
|
@ -12270,6 +12459,11 @@ packages:
|
||||||
|
|
||||||
/tslib/2.4.1:
|
/tslib/2.4.1:
|
||||||
resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
|
resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/tslib/2.5.0:
|
||||||
|
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/tsscmp/1.0.6:
|
/tsscmp/1.0.6:
|
||||||
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
||||||
|
@ -12675,7 +12869,7 @@ packages:
|
||||||
/validate-npm-package-license/3.0.4:
|
/validate-npm-package-license/3.0.4:
|
||||||
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||||
dependencies:
|
dependencies:
|
||||||
spdx-correct: 3.1.1
|
spdx-correct: 3.2.0
|
||||||
spdx-expression-parse: 3.0.1
|
spdx-expression-parse: 3.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -12711,13 +12905,13 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
fs-mkdirp-stream: 1.0.0
|
fs-mkdirp-stream: 1.0.0
|
||||||
glob-stream: 6.1.0
|
glob-stream: 6.1.0
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
is-valid-glob: 1.0.0
|
is-valid-glob: 1.0.0
|
||||||
lazystream: 1.0.1
|
lazystream: 1.0.1
|
||||||
lead: 1.0.0
|
lead: 1.0.0
|
||||||
object.assign: 4.1.4
|
object.assign: 4.1.4
|
||||||
pumpify: 1.5.1
|
pumpify: 1.5.1
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.8
|
||||||
remove-bom-buffer: 3.0.0
|
remove-bom-buffer: 3.0.0
|
||||||
remove-bom-stream: 1.2.0
|
remove-bom-stream: 1.2.0
|
||||||
resolve-options: 1.1.0
|
resolve-options: 1.1.0
|
||||||
|
@ -12734,7 +12928,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
append-buffer: 1.0.2
|
append-buffer: 1.0.2
|
||||||
convert-source-map: 1.9.0
|
convert-source-map: 1.9.0
|
||||||
graceful-fs: 4.2.10
|
graceful-fs: 4.2.11
|
||||||
normalize-path: 2.1.1
|
normalize-path: 2.1.1
|
||||||
now-and-later: 2.0.1
|
now-and-later: 2.0.1
|
||||||
remove-bom-buffer: 3.0.0
|
remove-bom-buffer: 3.0.0
|
||||||
|
@ -12858,9 +13052,9 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
axios: 0.25.0_debug@4.3.4
|
axios: 0.25.0_debug@4.3.4
|
||||||
joi: 17.7.0
|
joi: 17.9.1
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
minimist: 1.2.7
|
minimist: 1.2.8
|
||||||
rxjs: 7.8.0
|
rxjs: 7.8.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
|
|
Loading…
Reference in New Issue