wip
This commit is contained in:
@@ -2290,6 +2290,8 @@ _permissions:
|
|||||||
"read:clip-favorite": "クリップのいいねを見る"
|
"read:clip-favorite": "クリップのいいねを見る"
|
||||||
"read:federation": "連合に関する情報を取得する"
|
"read:federation": "連合に関する情報を取得する"
|
||||||
"write:report-abuse": "違反を報告する"
|
"write:report-abuse": "違反を報告する"
|
||||||
|
"write:chat": "チャットを操作する"
|
||||||
|
"read:chat": "チャットを閲覧する"
|
||||||
|
|
||||||
_auth:
|
_auth:
|
||||||
shareAccessTitle: "アプリへのアクセス許可"
|
shareAccessTitle: "アプリへのアクセス許可"
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
|
import { Brackets } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
@@ -17,6 +18,7 @@ import { PushNotificationService } from '@/core/PushNotificationService.js';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { ChatMessagesRepository, MiChatMessage, MiDriveFile, MiUser, UsersRepository } from '@/models/_.js';
|
import type { ChatMessagesRepository, MiChatMessage, MiDriveFile, MiUser, UsersRepository } from '@/models/_.js';
|
||||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||||
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ChatService {
|
export class ChatService {
|
||||||
@@ -41,6 +43,7 @@ export class ChatService {
|
|||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
private pushNotificationService: PushNotificationService,
|
private pushNotificationService: PushNotificationService,
|
||||||
private userBlockingService: UserBlockingService,
|
private userBlockingService: UserBlockingService,
|
||||||
|
private queryService: QueryService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,4 +254,28 @@ export class ChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async userTimeline(meId: MiUser['id'], otherId: MiUser['id'], sinceId: MiChatMessage['id'] | null, untilId: MiChatMessage['id'] | null, limit: number) {
|
||||||
|
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), sinceId, untilId)
|
||||||
|
.andWhere(new Brackets(qb => {
|
||||||
|
qb
|
||||||
|
.where(new Brackets(qb => {
|
||||||
|
qb
|
||||||
|
.where('message.fromUserId = :meId')
|
||||||
|
.andWhere('message.toUserId = :otherId');
|
||||||
|
}))
|
||||||
|
.orWhere(new Brackets(qb => {
|
||||||
|
qb
|
||||||
|
.where('message.fromUserId = :otherId')
|
||||||
|
.andWhere('message.toUserId = :meId');
|
||||||
|
}));
|
||||||
|
}))
|
||||||
|
.setParameter('meId', meId)
|
||||||
|
.setParameter('otherId', otherId);
|
||||||
|
|
||||||
|
const messages = await query.take(limit).getMany();
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -398,4 +398,5 @@ export * as 'users/search-by-username-and-host' from './endpoints/users/search-b
|
|||||||
export * as 'users/show' from './endpoints/users/show.js';
|
export * as 'users/show' from './endpoints/users/show.js';
|
||||||
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
|
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
|
||||||
export * as 'chat/messages/create' from './endpoints/chat/messages/create.js';
|
export * as 'chat/messages/create' from './endpoints/chat/messages/create.js';
|
||||||
|
export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js';
|
||||||
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
|
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
|
||||||
|
@@ -10,13 +10,15 @@ import { GetterService } from '@/server/api/GetterService.js';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { ApiError } from '@/server/api/error.js';
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { ChatService } from '@/core/ChatService.js';
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
import { DriveFilesRepository, MiUser } from '@/models/_.js';
|
import type { DriveFilesRepository, MiUser } from '@/models/_.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['chat'],
|
tags: ['chat'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
|
prohibitMoved: true,
|
||||||
|
|
||||||
kind: 'write:chat',
|
kind: 'write:chat',
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
@@ -80,21 +82,8 @@ export const paramDef = {
|
|||||||
properties: {
|
properties: {
|
||||||
text: { type: 'string', nullable: true, maxLength: 2000 },
|
text: { type: 'string', nullable: true, maxLength: 2000 },
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
},
|
},
|
||||||
anyOf: [
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
},
|
|
||||||
required: ['userId'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
groupId: { type: 'string', format: 'misskey:id' },
|
|
||||||
},
|
|
||||||
required: ['groupId'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -107,8 +96,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
private chatService: ChatService,
|
private chatService: ChatService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
let toUser: MiUser | null;
|
let file = null;
|
||||||
//let toGroup: UserGroup | null;
|
if (ps.fileId != null) {
|
||||||
|
file = await this.driveFilesRepository.findOneBy({
|
||||||
|
id: ps.fileId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// テキストが無いかつ添付ファイルも無かったらエラー
|
||||||
|
if (ps.text == null && file == null) {
|
||||||
|
throw new ApiError(meta.errors.contentRequired);
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.userId != null) {
|
if (ps.userId != null) {
|
||||||
// Myself
|
// Myself
|
||||||
@@ -116,11 +119,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.recipientIsYourself);
|
throw new ApiError(meta.errors.recipientIsYourself);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch recipient (user)
|
const toUser = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
toUser = await this.getterService.getUser(ps.userId).catch(err => {
|
|
||||||
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return await this.chatService.createMessage({
|
||||||
|
fromUser: me,
|
||||||
|
toUser,
|
||||||
|
text: ps.text,
|
||||||
|
file: file,
|
||||||
|
});
|
||||||
}/* else if (ps.groupId != null) {
|
}/* else if (ps.groupId != null) {
|
||||||
// Fetch recipient (group)
|
// Fetch recipient (group)
|
||||||
recipientGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId! });
|
recipientGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId! });
|
||||||
@@ -139,31 +148,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.groupAccessDenied);
|
throw new ApiError(meta.errors.groupAccessDenied);
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
let file = null;
|
|
||||||
if (ps.fileId != null) {
|
|
||||||
file = await this.driveFilesRepository.findOneBy({
|
|
||||||
id: ps.fileId,
|
|
||||||
userId: me.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (file == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// テキストが無いかつ添付ファイルも無かったらエラー
|
|
||||||
if (ps.text == null && file == null) {
|
|
||||||
throw new ApiError(meta.errors.contentRequired);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this.chatService.createMessage({
|
|
||||||
fromUser: me,
|
|
||||||
toUser,
|
|
||||||
toGroup,
|
|
||||||
text: ps.text,
|
|
||||||
file: file,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
|
import { ChatMessageEntityService } from '@/core/entities/ChatMessageEntityService.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['chat'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:chat',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'ChatMessageLite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
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' },
|
||||||
|
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private chatMessageEntityService: ChatMessageEntityService,
|
||||||
|
private chatService: ChatService,
|
||||||
|
private getterService: GetterService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
if (ps.userId != null) {
|
||||||
|
const other = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
const messages = await this.chatService.userTimeline(me.id, other.id, ps.sinceId, ps.untilId, ps.limit);
|
||||||
|
|
||||||
|
return await this.chatMessageEntityService.packLiteMany(messages);
|
||||||
|
}/* else if (ps.groupId != null) {
|
||||||
|
// Fetch recipient (group)
|
||||||
|
const recipientGroup = await this.userGroupRepository.findOneBy({ id: ps.groupId });
|
||||||
|
|
||||||
|
if (recipientGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check joined
|
||||||
|
const joining = await this.userGroupJoiningsRepository.findOneBy({
|
||||||
|
userId: me.id,
|
||||||
|
userGroupId: recipientGroup.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (joining == null) {
|
||||||
|
throw new ApiError(meta.errors.groupAccessDenied);
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = this.queryService.makePaginationQuery(this.messagingMessagesRepository.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) {
|
||||||
|
this.messagingService.readGroupMessagingMessage(me.id, recipientGroup.id, messages.map(x => x.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Promise.all(messages.map(message => this.messagingMessageEntityService.pack(message, me, {
|
||||||
|
populateGroup: false,
|
||||||
|
})));
|
||||||
|
}*/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -83,8 +83,8 @@ const mainChannel = stream.useChannel('main');
|
|||||||
``` ts
|
``` ts
|
||||||
const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' });
|
const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' });
|
||||||
|
|
||||||
const messagingChannel = stream.useChannel('messaging', {
|
const chatChannel = stream.useChannel('chat', {
|
||||||
otherparty: 'xxxxxxxxxx',
|
other: 'xxxxxxxxxx',
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -115,11 +115,11 @@ mainChannel.on('notification', notification => {
|
|||||||
|
|
||||||
``` ts
|
``` ts
|
||||||
const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' });
|
const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' });
|
||||||
const messagingChannel = stream.useChannel('messaging', {
|
const chatChannel = stream.useChannel('chat', {
|
||||||
otherparty: 'xxxxxxxxxx',
|
other: 'xxxxxxxxxx',
|
||||||
});
|
});
|
||||||
|
|
||||||
messagingChannel.send('read', {
|
chatChannel.send('read', {
|
||||||
id: 'xxxxxxxxxx'
|
id: 'xxxxxxxxxx'
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
@@ -37,8 +37,8 @@ export const permissions = [
|
|||||||
'write:favorites',
|
'write:favorites',
|
||||||
'read:following',
|
'read:following',
|
||||||
'write:following',
|
'write:following',
|
||||||
'read:messaging',
|
'read:messaging', // deprecated
|
||||||
'write:messaging',
|
'write:messaging', // deprecated
|
||||||
'read:mutes',
|
'read:mutes',
|
||||||
'write:mutes',
|
'write:mutes',
|
||||||
'write:notes',
|
'write:notes',
|
||||||
@@ -110,6 +110,8 @@ export const permissions = [
|
|||||||
'read:clip-favorite',
|
'read:clip-favorite',
|
||||||
'read:federation',
|
'read:federation',
|
||||||
'write:report-abuse',
|
'write:report-abuse',
|
||||||
|
'write:chat',
|
||||||
|
'read:chat',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const moderationLogTypes = [
|
export const moderationLogTypes = [
|
||||||
|
@@ -42,26 +42,26 @@ describe('Streaming', () => {
|
|||||||
test('useChannel with parameters', async () => {
|
test('useChannel with parameters', async () => {
|
||||||
const server = new WS('wss://misskey.test/streaming');
|
const server = new WS('wss://misskey.test/streaming');
|
||||||
const stream = new Stream('https://misskey.test', { token: 'TOKEN' });
|
const stream = new Stream('https://misskey.test', { token: 'TOKEN' });
|
||||||
const messagingChannelReceived: any[] = [];
|
const chatChannelReceived: any[] = [];
|
||||||
const messaging = stream.useChannel('messaging', { otherparty: 'aaa' });
|
const chat = stream.useChannel('chat', { other: 'aaa' });
|
||||||
messaging.on('message', payload => {
|
chat.on('message', payload => {
|
||||||
messagingChannelReceived.push(payload);
|
chatChannelReceived.push(payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
const ws = await server.connected;
|
const ws = await server.connected;
|
||||||
expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');
|
expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');
|
||||||
|
|
||||||
const msg = JSON.parse(await server.nextMessage as string);
|
const msg = JSON.parse(await server.nextMessage as string);
|
||||||
const messagingChannelId = msg.body.id;
|
const chatChannelId = msg.body.id;
|
||||||
expect(msg.type).toEqual('connect');
|
expect(msg.type).toEqual('connect');
|
||||||
expect(msg.body.channel).toEqual('messaging');
|
expect(msg.body.channel).toEqual('chat');
|
||||||
expect(msg.body.params).toEqual({ otherparty: 'aaa' });
|
expect(msg.body.params).toEqual({ other: 'aaa' });
|
||||||
expect(messagingChannelId != null).toEqual(true);
|
expect(chatChannelId != null).toEqual(true);
|
||||||
|
|
||||||
server.send(JSON.stringify({
|
server.send(JSON.stringify({
|
||||||
type: 'channel',
|
type: 'channel',
|
||||||
body: {
|
body: {
|
||||||
id: messagingChannelId,
|
id: chatChannelId,
|
||||||
type: 'message',
|
type: 'message',
|
||||||
body: {
|
body: {
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
@@ -69,7 +69,7 @@ describe('Streaming', () => {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
expect(messagingChannelReceived[0]).toEqual({
|
expect(chatChannelReceived[0]).toEqual({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,20 +81,20 @@ describe('Streaming', () => {
|
|||||||
const server = new WS('wss://misskey.test/streaming');
|
const server = new WS('wss://misskey.test/streaming');
|
||||||
const stream = new Stream('https://misskey.test', { token: 'TOKEN' });
|
const stream = new Stream('https://misskey.test', { token: 'TOKEN' });
|
||||||
|
|
||||||
stream.useChannel('messaging', { otherparty: 'aaa' });
|
stream.useChannel('chat', { other: 'aaa' });
|
||||||
stream.useChannel('messaging', { otherparty: 'bbb' });
|
stream.useChannel('chat', { other: 'bbb' });
|
||||||
|
|
||||||
const ws = await server.connected;
|
const ws = await server.connected;
|
||||||
expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');
|
expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');
|
||||||
|
|
||||||
const msg = JSON.parse(await server.nextMessage as string);
|
const msg = JSON.parse(await server.nextMessage as string);
|
||||||
const messagingChannelId = msg.body.id;
|
const chatChannelId = msg.body.id;
|
||||||
const msg2 = JSON.parse(await server.nextMessage as string);
|
const msg2 = JSON.parse(await server.nextMessage as string);
|
||||||
const messagingChannelId2 = msg2.body.id;
|
const chatChannelId2 = msg2.body.id;
|
||||||
|
|
||||||
expect(messagingChannelId != null).toEqual(true);
|
expect(chatChannelId != null).toEqual(true);
|
||||||
expect(messagingChannelId2 != null).toEqual(true);
|
expect(chatChannelId2 != null).toEqual(true);
|
||||||
expect(messagingChannelId).not.toEqual(messagingChannelId2);
|
expect(chatChannelId).not.toEqual(chatChannelId2);
|
||||||
|
|
||||||
stream.close();
|
stream.close();
|
||||||
server.close();
|
server.close();
|
||||||
@@ -104,8 +104,8 @@ describe('Streaming', () => {
|
|||||||
const server = new WS('wss://misskey.test/streaming');
|
const server = new WS('wss://misskey.test/streaming');
|
||||||
const stream = new Stream('https://misskey.test', { token: 'TOKEN' });
|
const stream = new Stream('https://misskey.test', { token: 'TOKEN' });
|
||||||
|
|
||||||
const messaging = stream.useChannel('messaging', { otherparty: 'aaa' });
|
const chat = stream.useChannel('chat', { other: 'aaa' });
|
||||||
messaging.send('read', { id: 'aaa' });
|
chat.send('read', { id: 'aaa' });
|
||||||
|
|
||||||
const ws = await server.connected;
|
const ws = await server.connected;
|
||||||
expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');
|
expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');
|
||||||
|
Reference in New Issue
Block a user