@towns-protocol/bot 0.0.412 → 0.0.414
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bot.d.ts +47 -13
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +108 -13
- package/dist/bot.js.map +1 -1
- package/dist/bot.test.js +153 -58
- package/dist/bot.test.js.map +1 -1
- package/package.json +8 -8
package/dist/bot.test.js
CHANGED
|
@@ -13,6 +13,7 @@ import { Hono } from 'hono';
|
|
|
13
13
|
import { randomUUID } from 'crypto';
|
|
14
14
|
import { getBalance, readContract, waitForTransactionReceipt } from 'viem/actions';
|
|
15
15
|
import townsAppAbi from '@towns-protocol/generated/dev/abis/ITownsApp.abi';
|
|
16
|
+
import channelsFacetAbi from '@towns-protocol/generated/dev/abis/Channels.abi';
|
|
16
17
|
import { parseEther } from 'viem';
|
|
17
18
|
import { execute } from 'viem/experimental/erc7821';
|
|
18
19
|
const log = dlog('test:bot');
|
|
@@ -22,6 +23,7 @@ const SLASH_COMMANDS = [
|
|
|
22
23
|
{ name: 'status', description: 'Check bot status' },
|
|
23
24
|
];
|
|
24
25
|
describe('Bot', { sequential: true }, () => {
|
|
26
|
+
const subscriptions = [];
|
|
25
27
|
const townsConfig = townsEnv().makeTownsConfig();
|
|
26
28
|
const bob = new SyncAgentTest(undefined, townsConfig);
|
|
27
29
|
const appRegistryDapp = new AppRegistryDapp(townsConfig.base.chainConfig, makeBaseProvider(townsConfig));
|
|
@@ -59,6 +61,10 @@ describe('Bot', { sequential: true }, () => {
|
|
|
59
61
|
client.debugForceMakeMiniblock(channelId, { forceSnapshot: true }),
|
|
60
62
|
]));
|
|
61
63
|
});
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
subscriptions.forEach((unsub) => unsub());
|
|
66
|
+
subscriptions.splice(0, subscriptions.length);
|
|
67
|
+
});
|
|
62
68
|
const setForwardSetting = async (forwardSetting) => {
|
|
63
69
|
await appRegistryRpcClient.setAppSettings({
|
|
64
70
|
appId: bin_fromHexString(botClientAddress),
|
|
@@ -117,6 +123,7 @@ describe('Bot', { sequential: true }, () => {
|
|
|
117
123
|
const cryptoStore = RiverDbManager.getCryptoDb(appAddress);
|
|
118
124
|
const botClient = new Client(signerContext, rpcClient, cryptoStore, new MockEntitlementsDelegate());
|
|
119
125
|
await expect(botClient.initializeUser({ appAddress, skipSync: true })).resolves.toBeDefined();
|
|
126
|
+
await botClient.uploadDeviceKeys();
|
|
120
127
|
await bobClient.riverConnection.call((client) => client.joinUser(spaceId, botClient.userId));
|
|
121
128
|
await bobClient.riverConnection.call((client) => client.joinUser(channelId, botClient.userId));
|
|
122
129
|
const addResult = await botClient.uploadDeviceKeys();
|
|
@@ -188,13 +195,34 @@ describe('Bot', { sequential: true }, () => {
|
|
|
188
195
|
expect(joined.has(botClientAddress)).toBe(true);
|
|
189
196
|
expect(joined.get(botClientAddress)?.appAddress).toBe(appAddress);
|
|
190
197
|
});
|
|
198
|
+
it('should be entitled', async () => {
|
|
199
|
+
const spaceDapp = bobClient.riverConnection.spaceDapp;
|
|
200
|
+
const isInstalled = await spaceDapp.isAppInstalled(spaceId, bot.appAddress);
|
|
201
|
+
expect(isInstalled).toBe(true);
|
|
202
|
+
const isEntitledRead = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.Read);
|
|
203
|
+
const isEntitledWrite = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.Write);
|
|
204
|
+
const isEntitledReact = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.React);
|
|
205
|
+
const isEntitledModifyBanning = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.ModifyBanning);
|
|
206
|
+
const isEntitledModifySpaceSettings = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.ModifySpaceSettings);
|
|
207
|
+
const isEntitledRedact = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.Redact);
|
|
208
|
+
const isEntitledPinMessage = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.PinMessage);
|
|
209
|
+
const isEntitledAddRemove = await spaceDapp.isAppEntitled(spaceId, bot.botId, bot.appAddress, Permission.AddRemoveChannels);
|
|
210
|
+
expect(isEntitledRead).toBe(true);
|
|
211
|
+
expect(isEntitledWrite).toBe(true);
|
|
212
|
+
expect(isEntitledReact).toBe(true);
|
|
213
|
+
expect(isEntitledModifyBanning).toBe(true);
|
|
214
|
+
expect(isEntitledModifySpaceSettings).toBe(true);
|
|
215
|
+
expect(isEntitledRedact).toBe(true);
|
|
216
|
+
expect(isEntitledPinMessage).toBe(true);
|
|
217
|
+
expect(isEntitledAddRemove).toBe(true);
|
|
218
|
+
});
|
|
191
219
|
it('should receive a message forwarded', async () => {
|
|
192
220
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
193
221
|
const timeBeforeSendMessage = Date.now();
|
|
194
222
|
let receivedMessages = [];
|
|
195
|
-
bot.onMessage((_h, e) => {
|
|
223
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
196
224
|
receivedMessages.push(e);
|
|
197
|
-
});
|
|
225
|
+
}));
|
|
198
226
|
const TEST_MESSAGE = 'Hello bot!';
|
|
199
227
|
const { eventId } = await bobDefaultChannel.sendMessage(TEST_MESSAGE);
|
|
200
228
|
await waitFor(() => receivedMessages.length > 0, { timeoutMS: 15_000 });
|
|
@@ -222,9 +250,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
222
250
|
it('should not receive messages when forwarding is set to no messages', async () => {
|
|
223
251
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_NO_MESSAGES);
|
|
224
252
|
const receivedMessages = [];
|
|
225
|
-
bot.onMessage((_h, e) => {
|
|
253
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
226
254
|
receivedMessages.push(e);
|
|
227
|
-
});
|
|
255
|
+
}));
|
|
228
256
|
const TEST_MESSAGE = 'This message should not be forwarded';
|
|
229
257
|
await bobDefaultChannel.sendMessage(TEST_MESSAGE);
|
|
230
258
|
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
@@ -233,9 +261,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
233
261
|
it('should receive channel join event when alice joins the channel if bot is listening to channel join events', async () => {
|
|
234
262
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
235
263
|
const receivedChannelJoinEvents = [];
|
|
236
|
-
bot.onChannelJoin((_h, e) => {
|
|
264
|
+
subscriptions.push(bot.onChannelJoin((_h, e) => {
|
|
237
265
|
receivedChannelJoinEvents.push(e);
|
|
238
|
-
});
|
|
266
|
+
}));
|
|
239
267
|
await aliceClient.spaces.joinSpace(spaceId, alice.signer);
|
|
240
268
|
await waitFor(() => receivedChannelJoinEvents.length > 0);
|
|
241
269
|
expect(receivedChannelJoinEvents.find((x) => x.userId === alice.userId)).toBeDefined();
|
|
@@ -249,9 +277,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
249
277
|
it('should receive slash command messages', async () => {
|
|
250
278
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
251
279
|
const receivedMessages = [];
|
|
252
|
-
bot.onSlashCommand('help', (_h, e) => {
|
|
280
|
+
subscriptions.push(bot.onSlashCommand('help', (_h, e) => {
|
|
253
281
|
receivedMessages.push(e);
|
|
254
|
-
});
|
|
282
|
+
}));
|
|
255
283
|
const { eventId } = await bobDefaultChannel.sendMessage('/help', {
|
|
256
284
|
appClientAddress: bot.botId,
|
|
257
285
|
});
|
|
@@ -263,9 +291,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
263
291
|
it('should receive slash command in a thread', async () => {
|
|
264
292
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
265
293
|
const receivedMessages = [];
|
|
266
|
-
bot.onSlashCommand('help', (_h, e) => {
|
|
294
|
+
subscriptions.push(bot.onSlashCommand('help', (_h, e) => {
|
|
267
295
|
receivedMessages.push(e);
|
|
268
|
-
});
|
|
296
|
+
}));
|
|
269
297
|
const { eventId: threadId } = await bobDefaultChannel.sendMessage('starting a thread');
|
|
270
298
|
const { eventId } = await bobDefaultChannel.sendMessage('/help', {
|
|
271
299
|
appClientAddress: bot.botId,
|
|
@@ -280,9 +308,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
280
308
|
it('should receive slash command as a reply', async () => {
|
|
281
309
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
282
310
|
const receivedMessages = [];
|
|
283
|
-
bot.onSlashCommand('help', (_h, e) => {
|
|
311
|
+
subscriptions.push(bot.onSlashCommand('help', (_h, e) => {
|
|
284
312
|
receivedMessages.push(e);
|
|
285
|
-
});
|
|
313
|
+
}));
|
|
286
314
|
const { eventId: replyId } = await bobDefaultChannel.sendMessage('yo');
|
|
287
315
|
const { eventId } = await bobDefaultChannel.sendMessage('/help', {
|
|
288
316
|
appClientAddress: bot.botId,
|
|
@@ -297,9 +325,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
297
325
|
it('should receive slash command with arguments', async () => {
|
|
298
326
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
299
327
|
const receivedMessages = [];
|
|
300
|
-
bot.onSlashCommand('status', (_h, e) => {
|
|
328
|
+
subscriptions.push(bot.onSlashCommand('status', (_h, e) => {
|
|
301
329
|
receivedMessages.push(e);
|
|
302
|
-
});
|
|
330
|
+
}));
|
|
303
331
|
const { eventId } = await bobDefaultChannel.sendMessage('/status detailed info', {
|
|
304
332
|
appClientAddress: bot.botId,
|
|
305
333
|
});
|
|
@@ -311,9 +339,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
311
339
|
it('onMessageEdit should be triggered when a message is edited', async () => {
|
|
312
340
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
313
341
|
const receivedEditEvents = [];
|
|
314
|
-
bot.onMessageEdit((_h, e) => {
|
|
342
|
+
subscriptions.push(bot.onMessageEdit((_h, e) => {
|
|
315
343
|
receivedEditEvents.push(e);
|
|
316
|
-
});
|
|
344
|
+
}));
|
|
317
345
|
const originalMessage = 'Original message to delete';
|
|
318
346
|
const editedMessage = 'Edited message content';
|
|
319
347
|
const { eventId: originalMessageId } = await bobDefaultChannel.sendMessage(originalMessage);
|
|
@@ -326,11 +354,11 @@ describe('Bot', { sequential: true }, () => {
|
|
|
326
354
|
it('onMessage should be triggered with threadId when a message is sent in a thread', async () => {
|
|
327
355
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
328
356
|
const receivedThreadMessages = [];
|
|
329
|
-
bot.onMessage((_h, e) => {
|
|
357
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
330
358
|
if (e.threadId) {
|
|
331
359
|
receivedThreadMessages.push(e);
|
|
332
360
|
}
|
|
333
|
-
});
|
|
361
|
+
}));
|
|
334
362
|
const initialMessage = 'Starting a thread';
|
|
335
363
|
const threadReply = 'Replying in thread';
|
|
336
364
|
const { eventId: initialMessageId } = await bobDefaultChannel.sendMessage(initialMessage);
|
|
@@ -347,11 +375,11 @@ describe('Bot', { sequential: true }, () => {
|
|
|
347
375
|
it('onMessage should be triggered with isMentioned when a bot is mentioned', async () => {
|
|
348
376
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
349
377
|
const receivedMentionedEvents = [];
|
|
350
|
-
bot.onMessage((_h, e) => {
|
|
378
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
351
379
|
if (e.isMentioned) {
|
|
352
380
|
receivedMentionedEvents.push(e);
|
|
353
381
|
}
|
|
354
|
-
});
|
|
382
|
+
}));
|
|
355
383
|
const TEST_MESSAGE = 'Hello @bot';
|
|
356
384
|
const { eventId } = await bobDefaultChannel.sendMessage(TEST_MESSAGE, {
|
|
357
385
|
mentions: [
|
|
@@ -372,9 +400,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
372
400
|
it('isMentioned should be false when someone else is mentioned', async () => {
|
|
373
401
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
374
402
|
const receivedMessages = [];
|
|
375
|
-
bot.onMessage((_h, e) => {
|
|
403
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
376
404
|
receivedMessages.push(e);
|
|
377
|
-
});
|
|
405
|
+
}));
|
|
378
406
|
const TEST_MESSAGE = 'Hello @alice';
|
|
379
407
|
const { eventId } = await bobDefaultChannel.sendMessage(TEST_MESSAGE, {
|
|
380
408
|
mentions: [
|
|
@@ -393,9 +421,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
393
421
|
it('onMessage should be triggered with both threadId and isMentioned when bot is mentioned in a thread', async () => {
|
|
394
422
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
395
423
|
const receivedMentionedInThreadEvents = [];
|
|
396
|
-
bot.onMessage((_h, e) => {
|
|
424
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
397
425
|
receivedMentionedInThreadEvents.push(e);
|
|
398
|
-
});
|
|
426
|
+
}));
|
|
399
427
|
const { eventId: initialMessageId } = await bobDefaultChannel.sendMessage('starting a thread');
|
|
400
428
|
const { eventId: threadMentionEventId } = await bobDefaultChannel.sendMessage('yo @bot check this thread', {
|
|
401
429
|
threadId: initialMessageId,
|
|
@@ -417,9 +445,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
417
445
|
it('thread message without bot mention should have isMentioned false', async () => {
|
|
418
446
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
419
447
|
const receivedMessages = [];
|
|
420
|
-
bot.onMessage((_h, e) => {
|
|
448
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
421
449
|
receivedMessages.push(e);
|
|
422
|
-
});
|
|
450
|
+
}));
|
|
423
451
|
const initialMessage = 'Starting another thread';
|
|
424
452
|
const threadMessageWithoutMention = 'Thread message without mention';
|
|
425
453
|
const { eventId: initialMessageId } = await bobDefaultChannel.sendMessage(initialMessage);
|
|
@@ -435,9 +463,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
435
463
|
it('onReaction should be triggered when a reaction is added', async () => {
|
|
436
464
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
437
465
|
const receivedReactionEvents = [];
|
|
438
|
-
bot.onReaction((_h, e) => {
|
|
466
|
+
subscriptions.push(bot.onReaction((_h, e) => {
|
|
439
467
|
receivedReactionEvents.push(e);
|
|
440
|
-
});
|
|
468
|
+
}));
|
|
441
469
|
const { eventId: messageId } = await bobClient.spaces
|
|
442
470
|
.getSpace(spaceId)
|
|
443
471
|
.getChannel(channelId)
|
|
@@ -452,9 +480,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
452
480
|
it('onRedaction should be triggered when a message is redacted', async () => {
|
|
453
481
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
454
482
|
const receivedRedactionEvents = [];
|
|
455
|
-
bot.onRedaction((_h, e) => {
|
|
483
|
+
subscriptions.push(bot.onRedaction((_h, e) => {
|
|
456
484
|
receivedRedactionEvents.push(e);
|
|
457
|
-
});
|
|
485
|
+
}));
|
|
458
486
|
const { eventId: messageId } = await bobDefaultChannel.sendMessage('Hello');
|
|
459
487
|
const { eventId: redactionId } = await bobDefaultChannel.redact(messageId);
|
|
460
488
|
await waitFor(() => receivedRedactionEvents.length > 0);
|
|
@@ -528,9 +556,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
528
556
|
it('bot can redact other people messages', async () => {
|
|
529
557
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
530
558
|
const messages = [];
|
|
531
|
-
bot.onMessage((_h, e) => {
|
|
559
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
532
560
|
messages.push(e);
|
|
533
|
-
});
|
|
561
|
+
}));
|
|
534
562
|
const { eventId: bobMessageId } = await bobDefaultChannel.sendMessage('Hello');
|
|
535
563
|
await waitFor(() => expect(bobDefaultChannel.timeline.events.value.find((x) => x.eventId === bobMessageId)
|
|
536
564
|
?.content?.kind).toBe(RiverTimelineEvent.ChannelMessage));
|
|
@@ -544,9 +572,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
544
572
|
it.skip('onMessage should be triggered with replyId when a message is replied to', async () => {
|
|
545
573
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_MENTIONS_REPLIES_REACTIONS);
|
|
546
574
|
const receivedReplyEvents = [];
|
|
547
|
-
bot.onMessage((_h, e) => {
|
|
575
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
548
576
|
receivedReplyEvents.push(e);
|
|
549
|
-
});
|
|
577
|
+
}));
|
|
550
578
|
const { eventId: messageId } = await bot.sendMessage(channelId, 'hii');
|
|
551
579
|
await waitFor(() => expect(bobDefaultChannel.timeline.events.value.find((x) => x.eventId === messageId)).toBeDefined());
|
|
552
580
|
const { eventId: replyEventId } = await bobDefaultChannel.sendMessage('hi back', {
|
|
@@ -560,9 +588,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
560
588
|
it('onTip should be triggered when a tip is received', async () => {
|
|
561
589
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
562
590
|
const receivedTipEvents = [];
|
|
563
|
-
bot.onTip((_h, e) => {
|
|
591
|
+
subscriptions.push(bot.onTip((_h, e) => {
|
|
564
592
|
receivedTipEvents.push(e);
|
|
565
|
-
});
|
|
593
|
+
}));
|
|
566
594
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
567
595
|
const { eventId: messageId } = await bot.sendMessage(channelId, 'hii');
|
|
568
596
|
const balanceBefore = (await ethersProvider.getBalance(appAddress)).toBigInt();
|
|
@@ -602,7 +630,7 @@ describe('Bot', { sequential: true }, () => {
|
|
|
602
630
|
it('bot can use sendTip() to send tips using app balance', async () => {
|
|
603
631
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
604
632
|
const receivedMessages = [];
|
|
605
|
-
bot.onMessage(async (handler, event) => {
|
|
633
|
+
subscriptions.push(bot.onMessage(async (handler, event) => {
|
|
606
634
|
const result = await handler.sendTip({
|
|
607
635
|
userId: bob.userId,
|
|
608
636
|
amount: ethers.utils.parseUnits('0.005').toBigInt(),
|
|
@@ -612,7 +640,7 @@ describe('Bot', { sequential: true }, () => {
|
|
|
612
640
|
expect(result.txHash).toBeDefined();
|
|
613
641
|
expect(result.eventId).toBeDefined();
|
|
614
642
|
receivedMessages.push(event);
|
|
615
|
-
});
|
|
643
|
+
}));
|
|
616
644
|
const bobBalanceBefore = (await ethersProvider.getBalance(bob.userId)).toBigInt();
|
|
617
645
|
// Bob sends a message asking for a tip
|
|
618
646
|
const { eventId: bobMessageId } = await bobDefaultChannel.sendMessage('Tip me please!');
|
|
@@ -624,9 +652,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
624
652
|
it('onEventRevoke (FORWARD_SETTING_ALL_MESSAGES) should be triggered when a message is revoked', async () => {
|
|
625
653
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
626
654
|
const receivedEventRevokeEvents = [];
|
|
627
|
-
bot.onEventRevoke((_h, e) => {
|
|
655
|
+
subscriptions.push(bot.onEventRevoke((_h, e) => {
|
|
628
656
|
receivedEventRevokeEvents.push(e);
|
|
629
|
-
});
|
|
657
|
+
}));
|
|
630
658
|
const { eventId: messageId } = await bot.sendMessage(channelId, 'hii');
|
|
631
659
|
await bobDefaultChannel.adminRedact(messageId);
|
|
632
660
|
await waitFor(() => receivedEventRevokeEvents.length > 0);
|
|
@@ -635,9 +663,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
635
663
|
it.fails('onEventRevoke (FORWARD_SETTING_MENTIONS_REPLIES_REACTIONS) should be triggered when a message that mentions the bot is revoked', async () => {
|
|
636
664
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_MENTIONS_REPLIES_REACTIONS);
|
|
637
665
|
const receivedEventRevokeEvents = [];
|
|
638
|
-
bot.onEventRevoke((_h, e) => {
|
|
666
|
+
subscriptions.push(bot.onEventRevoke((_h, e) => {
|
|
639
667
|
receivedEventRevokeEvents.push(e);
|
|
640
|
-
});
|
|
668
|
+
}));
|
|
641
669
|
const { eventId: messageId } = await bobDefaultChannel.sendMessage('hii @bot', {
|
|
642
670
|
mentions: [
|
|
643
671
|
{
|
|
@@ -656,9 +684,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
656
684
|
await appRegistryDapp.uninstallApp(bob.signer, appAddress, SpaceAddressFromSpaceId(spaceId));
|
|
657
685
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
658
686
|
const receivedMentionedEvents = [];
|
|
659
|
-
bot.onMessage((_h, e) => {
|
|
687
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
660
688
|
receivedMentionedEvents.push(e);
|
|
661
|
-
});
|
|
689
|
+
}));
|
|
662
690
|
const TEST_MESSAGE = 'wont be received';
|
|
663
691
|
const { eventId } = await bobDefaultChannel.sendMessage(TEST_MESSAGE);
|
|
664
692
|
await expect(waitFor(() => receivedMentionedEvents.length > 0)).rejects.toThrow();
|
|
@@ -865,9 +893,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
865
893
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
866
894
|
const messageSchema = z.object({ text: z.string(), count: z.number() });
|
|
867
895
|
const receivedGmEvents = [];
|
|
868
|
-
bot.onGmMessage('test.typed.v1', messageSchema, (_h, e) => {
|
|
896
|
+
subscriptions.push(bot.onGmMessage('test.typed.v1', messageSchema, (_h, e) => {
|
|
869
897
|
receivedGmEvents.push({ typeUrl: e.typeUrl, data: e.data });
|
|
870
|
-
});
|
|
898
|
+
}));
|
|
871
899
|
const testData = { text: 'Hello', count: 42 };
|
|
872
900
|
// Bob sends the message so bot receives it (bot filters its own messages)
|
|
873
901
|
const jsonString = superjsonStringify(testData);
|
|
@@ -910,12 +938,12 @@ describe('Bot', { sequential: true }, () => {
|
|
|
910
938
|
const schema2 = z.object({ type: z.literal('type2'), text: z.string() });
|
|
911
939
|
const receivedType1 = [];
|
|
912
940
|
const receivedType2 = [];
|
|
913
|
-
bot.onGmMessage('test.multi.type1', schema1, (_h, e) => {
|
|
941
|
+
subscriptions.push(bot.onGmMessage('test.multi.type1', schema1, (_h, e) => {
|
|
914
942
|
receivedType1.push(e.data);
|
|
915
|
-
});
|
|
916
|
-
bot.onGmMessage('test.multi.type2', schema2, (_h, e) => {
|
|
943
|
+
}));
|
|
944
|
+
subscriptions.push(bot.onGmMessage('test.multi.type2', schema2, (_h, e) => {
|
|
917
945
|
receivedType2.push(e.data);
|
|
918
|
-
});
|
|
946
|
+
}));
|
|
919
947
|
const data1 = { type: 'type1', value: 123 };
|
|
920
948
|
const data2 = { type: 'type2', text: 'hello' };
|
|
921
949
|
await bobClient.riverConnection.call((client) => client.sendChannelMessage_GM(channelId, {
|
|
@@ -937,9 +965,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
937
965
|
it('should handle raw GM messages', async () => {
|
|
938
966
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
939
967
|
const receivedMessages = [];
|
|
940
|
-
bot.onRawGmMessage((_h, e) => {
|
|
968
|
+
subscriptions.push(bot.onRawGmMessage((_h, e) => {
|
|
941
969
|
receivedMessages.push({ typeUrl: e.typeUrl, message: e.message });
|
|
942
|
-
});
|
|
970
|
+
}));
|
|
943
971
|
const message = new TextEncoder().encode('Hello, world!');
|
|
944
972
|
await bobClient.riverConnection.call((client) => client.sendChannelMessage_GM(channelId, {
|
|
945
973
|
content: {
|
|
@@ -984,9 +1012,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
984
1012
|
it('should log error and continue processing if throws an error when handling an event', async () => {
|
|
985
1013
|
const consoleErrorSpy = vi.spyOn(console, 'error');
|
|
986
1014
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
987
|
-
|
|
1015
|
+
subscriptions.push(bot.onMessage(() => {
|
|
988
1016
|
throw new Error('test error');
|
|
989
|
-
}))
|
|
1017
|
+
}));
|
|
990
1018
|
await bobDefaultChannel.sendMessage('lol');
|
|
991
1019
|
await waitFor(() => consoleErrorSpy.mock.calls.length > 0);
|
|
992
1020
|
expect(consoleErrorSpy.mock.calls[0][0]).toContain('[@towns-protocol/bot] Error while handling event');
|
|
@@ -1024,6 +1052,73 @@ describe('Bot', { sequential: true }, () => {
|
|
|
1024
1052
|
?.kind).toBe(RiverTimelineEvent.ChannelMessage);
|
|
1025
1053
|
}, { timeoutMS: 20000 });
|
|
1026
1054
|
});
|
|
1055
|
+
it('bot can create channel, channel has the role, bob joins and sends message, bot receives message', async () => {
|
|
1056
|
+
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES);
|
|
1057
|
+
const streamEvents = [];
|
|
1058
|
+
const receivedMessages = [];
|
|
1059
|
+
subscriptions.push(bot.onStreamEvent((_h, e) => {
|
|
1060
|
+
log('stream event', e);
|
|
1061
|
+
streamEvents.push(e);
|
|
1062
|
+
}));
|
|
1063
|
+
subscriptions.push(bot.onMessage((_h, e) => {
|
|
1064
|
+
receivedMessages.push(e);
|
|
1065
|
+
}));
|
|
1066
|
+
const spaceDapp = bobClient.riverConnection.spaceDapp;
|
|
1067
|
+
const testNft1Address = await TestERC721.getContractAddress('TestNFT1');
|
|
1068
|
+
const ruleData = getNftRuleData(testNft1Address);
|
|
1069
|
+
const permissions = [Permission.Read, Permission.Write];
|
|
1070
|
+
const roleName = `TestRole for bot channel bot wallet`;
|
|
1071
|
+
// Bob creates a role with Read permission
|
|
1072
|
+
const txn = await spaceDapp.createRole(spaceId, roleName, permissions, [], ruleData, bob.signer);
|
|
1073
|
+
const { roleId, error: roleError } = await waitForRoleCreated(spaceDapp, spaceId, txn);
|
|
1074
|
+
expect(roleError).toBeUndefined();
|
|
1075
|
+
check(isDefined(roleId), 'roleId is defined');
|
|
1076
|
+
log('bob created role', roleId);
|
|
1077
|
+
// Bot creates a new channel
|
|
1078
|
+
const newChannelId = await bot.createChannel(spaceId, {
|
|
1079
|
+
name: `test-channel-with-role-bot`,
|
|
1080
|
+
description: `Channel with role created by bot with bot wallet`,
|
|
1081
|
+
});
|
|
1082
|
+
log(`bot created channel`, newChannelId);
|
|
1083
|
+
// Query the roles assigned to the channel
|
|
1084
|
+
const channelRoles = await readContract(bot.viem, {
|
|
1085
|
+
address: SpaceAddressFromSpaceId(spaceId),
|
|
1086
|
+
abi: channelsFacetAbi,
|
|
1087
|
+
functionName: 'getRolesByChannel',
|
|
1088
|
+
args: [
|
|
1089
|
+
newChannelId.startsWith('0x')
|
|
1090
|
+
? newChannelId
|
|
1091
|
+
: `0x${newChannelId}`,
|
|
1092
|
+
],
|
|
1093
|
+
});
|
|
1094
|
+
log('channel roles', channelRoles);
|
|
1095
|
+
// Verify the created role is included in the channel's roles
|
|
1096
|
+
expect(channelRoles).toContain(BigInt(roleId));
|
|
1097
|
+
// Bob joins the new channel
|
|
1098
|
+
await bobClient.riverConnection.call((client) => client.joinStream(newChannelId));
|
|
1099
|
+
// Bob gets the channel
|
|
1100
|
+
const bobNewChannel = bobClient.spaces.getSpace(spaceId).getChannel(newChannelId);
|
|
1101
|
+
await waitFor(() => bobNewChannel.value.status !== 'loading', { timeoutMS: 10000 });
|
|
1102
|
+
// Bob sends a message in the new channel
|
|
1103
|
+
const testMessage = `Hello bot in new channel`;
|
|
1104
|
+
log('bob sending message in new channel', {
|
|
1105
|
+
testMessage,
|
|
1106
|
+
botId: bot.botId,
|
|
1107
|
+
appAddress: bot.appAddress,
|
|
1108
|
+
bobUserId: bob.userId,
|
|
1109
|
+
});
|
|
1110
|
+
const { eventId } = await bobNewChannel.sendMessage(testMessage);
|
|
1111
|
+
log('bob sent message in new channel', eventId);
|
|
1112
|
+
await waitFor(() => streamEvents.length > 0, { timeoutMS: 15000 });
|
|
1113
|
+
await waitFor(() => expect(streamEvents.find((x) => x.eventId === eventId)).toBeDefined());
|
|
1114
|
+
// Wait for bot to receive the message
|
|
1115
|
+
await waitFor(() => receivedMessages.length > 0, { timeoutMS: 15000 });
|
|
1116
|
+
const receivedEvent = receivedMessages.find((x) => x.eventId === eventId);
|
|
1117
|
+
expect(receivedEvent).toBeDefined();
|
|
1118
|
+
expect(receivedEvent?.message).toBe(testMessage);
|
|
1119
|
+
expect(receivedEvent?.channelId).toBe(newChannelId);
|
|
1120
|
+
expect(receivedEvent?.userId).toBe(bobClient.userId);
|
|
1121
|
+
});
|
|
1027
1122
|
it('bot should be able to send encrypted interaction request and user should send encrypted response', async () => {
|
|
1028
1123
|
await setForwardSetting(ForwardSettingValue.FORWARD_SETTING_MENTIONS_REPLIES_REACTIONS);
|
|
1029
1124
|
const requestId = randomUUID();
|
|
@@ -1074,9 +1169,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
1074
1169
|
},
|
|
1075
1170
|
};
|
|
1076
1171
|
const receivedInteractionResponses = [];
|
|
1077
|
-
bot.onInteractionResponse((_h, e) => {
|
|
1172
|
+
subscriptions.push(bot.onInteractionResponse((_h, e) => {
|
|
1078
1173
|
receivedInteractionResponses.push(e.response);
|
|
1079
|
-
});
|
|
1174
|
+
}));
|
|
1080
1175
|
await bobClient.riverConnection.call(async (client) => {
|
|
1081
1176
|
// from the client, to the channel, encrypted so that only the bot can read it
|
|
1082
1177
|
return await client.sendInteractionResponse(channelId, recipient, interactionResponsePayload, encryptionDevice);
|
|
@@ -1143,9 +1238,9 @@ describe('Bot', { sequential: true }, () => {
|
|
|
1143
1238
|
},
|
|
1144
1239
|
};
|
|
1145
1240
|
const receivedInteractionResponses = [];
|
|
1146
|
-
bot.onInteractionResponse((_h, e) => {
|
|
1241
|
+
subscriptions.push(bot.onInteractionResponse((_h, e) => {
|
|
1147
1242
|
receivedInteractionResponses.push(e.response);
|
|
1148
|
-
});
|
|
1243
|
+
}));
|
|
1149
1244
|
await bobClient.riverConnection.call(async (client) => {
|
|
1150
1245
|
return await client.sendInteractionResponse(channelId, recipient, interactionResponsePayload, bot.getUserDevice());
|
|
1151
1246
|
});
|