@takaro/modules 0.0.10 → 0.0.13

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.
Files changed (41) hide show
  1. package/dist/dto/takaroEvents.d.ts +0 -3
  2. package/dist/dto/takaroEvents.js +1 -13
  3. package/dist/dto/takaroEvents.js.map +1 -1
  4. package/dist/main.js +2 -0
  5. package/dist/main.js.map +1 -1
  6. package/dist/modules/economyUtils/commands/shop.d.ts +1 -0
  7. package/dist/modules/economyUtils/commands/shop.js +69 -0
  8. package/dist/modules/economyUtils/commands/shop.js.map +1 -0
  9. package/dist/modules/economyUtils/commands/transfer.js +1 -1
  10. package/dist/modules/economyUtils/commands/transfer.js.map +1 -1
  11. package/dist/modules/economyUtils/index.js +29 -0
  12. package/dist/modules/economyUtils/index.js.map +1 -1
  13. package/dist/modules/serverMessages/cronJobs/Automated message.js +35 -3
  14. package/dist/modules/serverMessages/cronJobs/Automated message.js.map +1 -1
  15. package/dist/modules/timedShutdown/cronJobs/Shutdown.d.ts +1 -0
  16. package/dist/modules/timedShutdown/cronJobs/Shutdown.js +7 -0
  17. package/dist/modules/timedShutdown/cronJobs/Shutdown.js.map +1 -0
  18. package/dist/modules/timedShutdown/cronJobs/warning.d.ts +1 -0
  19. package/dist/modules/timedShutdown/cronJobs/warning.js +10 -0
  20. package/dist/modules/timedShutdown/cronJobs/warning.js.map +1 -0
  21. package/dist/modules/timedShutdown/index.d.ts +4 -0
  22. package/dist/modules/timedShutdown/index.js +33 -0
  23. package/dist/modules/timedShutdown/index.js.map +1 -0
  24. package/dist/modules.json +53 -2
  25. package/package.json +1 -1
  26. package/scripts/buildBuiltinJson.ts +0 -1
  27. package/src/__tests__/aliases.integration.test.ts +40 -0
  28. package/src/__tests__/bugRepros.integration.test.ts +163 -1
  29. package/src/__tests__/{economyUtils.shop.integration.test.ts → economy/claim.integration.test.ts} +10 -120
  30. package/src/__tests__/{economyUtils.integration.test.ts → economy/economyUtils.integration.test.ts} +2 -2
  31. package/src/__tests__/economy/shop.integration.test.ts +153 -0
  32. package/src/__tests__/serverMessages.integration.test.ts +32 -18
  33. package/src/dto/takaroEvents.ts +0 -10
  34. package/src/main.ts +2 -0
  35. package/src/modules/economyUtils/commands/shop.js +87 -0
  36. package/src/modules/economyUtils/commands/transfer.js +1 -1
  37. package/src/modules/economyUtils/index.ts +29 -0
  38. package/src/modules/serverMessages/cronJobs/Automated message.js +39 -4
  39. package/src/modules/timedShutdown/cronJobs/Shutdown.js +8 -0
  40. package/src/modules/timedShutdown/cronJobs/warning.js +13 -0
  41. package/src/modules/timedShutdown/index.ts +38 -0
@@ -40,6 +40,46 @@ const tests = [
40
40
  playerId: this.setupData.players[0].id,
41
41
  });
42
42
 
43
+ expect((await events).length).to.be.eq(1);
44
+ expect((await events)[0].data.meta.msg).to.be.eq('Teleported to test.');
45
+ },
46
+ }),
47
+ new IntegrationTest<IModuleTestsSetupData>({
48
+ group,
49
+ snapshot: false,
50
+ setup: modulesTestSetup,
51
+ name: 'Aliases are case insensitive',
52
+ test: async function () {
53
+ await this.client.gameserver.gameServerControllerInstallModule(
54
+ this.setupData.gameserver.id,
55
+ this.setupData.teleportsModule.id,
56
+ {
57
+ systemConfig: JSON.stringify({
58
+ commands: {
59
+ teleport: {
60
+ delay: 0,
61
+ aliases: ['tp', 'tellyport'],
62
+ },
63
+ },
64
+ }),
65
+ },
66
+ );
67
+
68
+ const setEvents = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
69
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
70
+ msg: '/settp test',
71
+ playerId: this.setupData.players[0].id,
72
+ });
73
+
74
+ expect((await setEvents).length).to.be.eq(1);
75
+ expect((await setEvents)[0].data.meta.msg).to.be.eq('Teleport test set.');
76
+
77
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
78
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
79
+ msg: '/TelLyPoRt test',
80
+ playerId: this.setupData.players[0].id,
81
+ });
82
+
43
83
  expect((await events).length).to.be.eq(1);
44
84
  expect((await events)[0].data.meta.msg).to.be.eq('Teleported to test.');
45
85
  },
@@ -1,5 +1,5 @@
1
1
  import { IntegrationTest, expect, IModuleTestsSetupData, modulesTestSetup, EventsAwaiter } from '@takaro/test';
2
- import { GameEvents } from '../dto/index.js';
2
+ import { GameEvents, HookEvents } from '../dto/index.js';
3
3
  import { EventChatMessageChannelEnum } from '@takaro/apiclient';
4
4
 
5
5
  const group = 'Bug repros';
@@ -63,6 +63,168 @@ const tests = [
63
63
  expect((await events).map((e) => e.data.meta.msg)).to.include.members(['First hook', 'Second hook']);
64
64
  },
65
65
  }),
66
+ // Before, the event "hook-executed" would not have the correct result logs saved
67
+ // The TakaroDTO validation was rejecting it because "not a string"
68
+ // `An instance of TakaroEventHookExecuted has failed the validation:\n - property result.logs[1].msg has failed the following constraints: isString \n - property result.logs[2].msg has failed the following constraints: isString \n"`
69
+ new IntegrationTest<IModuleTestsSetupData>({
70
+ group,
71
+ snapshot: false,
72
+ name: 'Bug repro: console.log in a function works with objects',
73
+ setup: modulesTestSetup,
74
+ test: async function () {
75
+ const mod = (
76
+ await this.client.module.moduleControllerCreate({
77
+ name: 'Test module',
78
+ })
79
+ ).data.data;
80
+ await this.client.hook.hookControllerCreate({
81
+ name: 'Test hook 1',
82
+ moduleId: mod.id,
83
+ regex: 'test msg',
84
+ eventType: 'chat-message',
85
+ function: `import { data, takaro } from '@takaro/helpers';
86
+ async function main() {
87
+ console.log("foo");
88
+ console.log({ foo: "bar" });
89
+ console.log(["baz", 1]);
90
+ }
91
+ await main();`,
92
+ });
93
+
94
+ await this.client.gameserver.gameServerControllerInstallModule(this.setupData.gameserver.id, mod.id);
95
+
96
+ const listener = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.HOOK_EXECUTED, 1);
97
+
98
+ await this.client.hook.hookControllerTrigger({
99
+ eventType: 'chat-message',
100
+ gameServerId: this.setupData.gameserver.id,
101
+ moduleId: mod.id,
102
+ playerId: this.setupData.players[0].id,
103
+ eventMeta: {
104
+ msg: 'test msg',
105
+ channel: EventChatMessageChannelEnum.Global,
106
+ },
107
+ });
108
+
109
+ const result = (await listener)[0].data.meta.result;
110
+ expect(result.success).to.be.true;
111
+ const logs = result.logs;
112
+ const msgs = logs.map((l: any) => l.msg);
113
+ expect(msgs).to.include.members(['foo', JSON.stringify({ foo: 'bar' }), JSON.stringify(['baz', 1])]);
114
+ },
115
+ }),
116
+ /**
117
+ * Repro for https://github.com/gettakaro/takaro/issues/1047
118
+ * System config uses the names of functions to create structure
119
+ * If a module is installed, and you then rename the function, the system config will be broken
120
+ */
121
+ new IntegrationTest<IModuleTestsSetupData>({
122
+ group,
123
+ snapshot: false,
124
+ name: 'Bug repro: renaming functions breaks system config #1047',
125
+ setup: modulesTestSetup,
126
+ test: async function () {
127
+ const mod = (
128
+ await this.client.module.moduleControllerCreate({
129
+ name: 'Test module',
130
+ })
131
+ ).data.data;
132
+ const createdCommand = await this.client.command.commandControllerCreate({
133
+ name: 'testcmd',
134
+ moduleId: mod.id,
135
+ trigger: 'testpong',
136
+ function: `import { data, takaro } from '@takaro/helpers';
137
+ async function main() {
138
+ const { player } = data;
139
+ await takaro.gameserver.gameServerControllerSendMessage(data.gameServerId, {
140
+ message: 'pong',
141
+ });
142
+ }
143
+ await main();`,
144
+ });
145
+
146
+ await this.client.gameserver.gameServerControllerInstallModule(this.setupData.gameserver.id, mod.id, {
147
+ userConfig: JSON.stringify({}),
148
+ systemConfig: JSON.stringify({ commands: { testcmd: { aliases: ['foobar'] } } }),
149
+ });
150
+
151
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
152
+
153
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
154
+ msg: '/foobar',
155
+ playerId: this.setupData.players[0].id,
156
+ });
157
+
158
+ expect((await events).length).to.be.eq(1);
159
+ expect((await events)[0].data.meta.msg).to.be.eq('pong');
160
+
161
+ // Rename the function
162
+ await this.client.command.commandControllerUpdate(createdCommand.data.data.id, { name: 'testcmd2' });
163
+
164
+ const eventsAfter = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
165
+
166
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
167
+ msg: '/foobar',
168
+ playerId: this.setupData.players[0].id,
169
+ });
170
+
171
+ expect((await eventsAfter).length).to.be.eq(1);
172
+ expect((await eventsAfter)[0].data.meta.msg).to.be.eq('pong');
173
+ },
174
+ }),
175
+ /**
176
+ * Repro for https://github.com/gettakaro/takaro/issues/1405
177
+ * When providing invalid code, Takaro trips over itself and throws a nasty error
178
+ * Instead, it should catch it properly and return an actionable message for the user
179
+ */
180
+ new IntegrationTest<IModuleTestsSetupData>({
181
+ group,
182
+ snapshot: false,
183
+ name: 'Bug repro: invalid module code throws bad error #1405',
184
+ setup: modulesTestSetup,
185
+ test: async function () {
186
+ const mod = (
187
+ await this.client.module.moduleControllerCreate({
188
+ name: 'Test module',
189
+ })
190
+ ).data.data;
191
+ await this.client.command.commandControllerCreate({
192
+ name: 'testcmd',
193
+ moduleId: mod.id,
194
+ trigger: 'test',
195
+ // Yes, very bad code! This is what a user might accidentally do
196
+ function: `import { data, takaro } from '@takaro/helpers';
197
+
198
+ const messed = (await takaro.variable.variableControllerSearch({ search: { key: [\`object Object\`] } })).data.data
199
+ for (const thisVar of messed) {
200
+ let originalString = thisVar.key;
201
+ let newString = originalString.replace("[object Object]", "2024-09-20");
202
+
203
+ await takaro.variable.variableControllerUpdate(thisVar.id, { key: newString, value: thisVar.value });
204
+
205
+ }
206
+ data.player.pm(\`done\`).
207
+ `,
208
+ });
209
+
210
+ await this.client.gameserver.gameServerControllerInstallModule(this.setupData.gameserver.id, mod.id, {
211
+ userConfig: JSON.stringify({}),
212
+ systemConfig: JSON.stringify({}),
213
+ });
214
+
215
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.COMMAND_EXECUTED, 1);
216
+
217
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
218
+ msg: '/test',
219
+ playerId: this.setupData.players[0].id,
220
+ });
221
+
222
+ console.log(JSON.stringify((await events)[0].data.meta.result, null, 2));
223
+ const result = (await events)[0].data.meta.result;
224
+ expect(result.success).to.be.false;
225
+ expect(result.reason).to.include('SyntaxError: Unexpected end of input.');
226
+ },
227
+ }),
66
228
  ];
67
229
 
68
230
  describe(group, function () {
@@ -1,123 +1,13 @@
1
- import { faker } from '@faker-js/faker';
2
- import { Client, ShopListingOutputDTO, UserOutputDTO } from '@takaro/apiclient';
1
+ import { EventsAwaiter, expect, IntegrationTest, IShopSetup, shopSetup } from '@takaro/test';
2
+ import { HookEvents } from '../../dto/index.js';
3
3
 
4
- import {
5
- EventsAwaiter,
6
- integrationConfig,
7
- expect,
8
- modulesTestSetup,
9
- IModuleTestsSetupData,
10
- IntegrationTest,
11
- } from '@takaro/test';
12
- import { HookEvents } from '../dto/index.js';
13
-
14
- const group = 'EconomyUtils:Shop';
15
-
16
- interface IShopSetup extends IModuleTestsSetupData {
17
- userClient: Client;
18
- listing100: ShopListingOutputDTO;
19
- listing33: ShopListingOutputDTO;
20
- }
21
-
22
- async function createUserForPlayer(client: Client, playerId: string, gameServerId: string) {
23
- const password = 'shop-tester-password-very-safe';
24
-
25
- let user: UserOutputDTO | null = null;
26
-
27
- user = (
28
- await client.user.userControllerCreate({
29
- name: 'test',
30
- email: `test-${faker.internet.email()}`,
31
- password,
32
- })
33
- ).data.data;
34
-
35
- const userClient = new Client({ auth: { username: user.email, password }, url: integrationConfig.get('host') });
36
- await userClient.login();
37
-
38
- const eventsAwaiter = new EventsAwaiter();
39
- await eventsAwaiter.connect(client);
40
- const chatEventWaiter = eventsAwaiter.waitForEvents(HookEvents.CHAT_MESSAGE);
41
- await client.command.commandControllerTrigger(gameServerId, {
42
- msg: '/link',
43
- playerId,
44
- });
45
- const chatEvents = await chatEventWaiter;
46
- expect(chatEvents).to.have.length(1);
47
- const code = chatEvents[0].data.meta.msg.match(/code=(\w+-\w+-\w+)/)[1];
48
- await userClient.user.userControllerLinkPlayerProfile({ email: user.email, code });
49
-
50
- return {
51
- user,
52
- client: userClient,
53
- };
54
- }
55
-
56
- async function setupShop(client: Client, gameServerId: string) {
57
- const items = (await client.item.itemControllerSearch()).data.data;
58
-
59
- const listing100Res = await client.shopListing.shopListingControllerCreate({
60
- gameServerId: gameServerId,
61
- items: [{ itemId: items[0].id, amount: 1 }],
62
- price: 100,
63
- name: 'Test item',
64
- });
65
-
66
- const listing33Res = await client.shopListing.shopListingControllerCreate({
67
- gameServerId: gameServerId,
68
- items: [{ itemId: items[1].id, amount: 1 }],
69
- price: 33,
70
- name: 'Test item 2',
71
- });
72
-
73
- return {
74
- listing100: listing100Res.data.data,
75
- listing33: listing33Res.data.data,
76
- };
77
- }
78
-
79
- const customSetup = async function (this: IntegrationTest<IShopSetup>): Promise<IShopSetup> {
80
- const setupData = await modulesTestSetup.bind(this as unknown as IntegrationTest<IModuleTestsSetupData>)();
81
-
82
- await this.client.settings.settingsControllerSet('economyEnabled', {
83
- value: 'true',
84
- gameServerId: setupData.gameserver.id,
85
- });
86
-
87
- await this.client.settings.settingsControllerSet('economyEnabled', {
88
- value: 'true',
89
- });
90
- await this.client.settings.settingsControllerSet('currencyName', {
91
- gameServerId: setupData.gameserver.id,
92
- value: 'test coin',
93
- });
94
-
95
- await this.client.gameserver.gameServerControllerInstallModule(
96
- setupData.gameserver.id,
97
- setupData.economyUtilsModule.id,
98
- );
99
-
100
- const { client: userClient } = await createUserForPlayer(
101
- this.client,
102
- setupData.players[0].id,
103
- setupData.gameserver.id,
104
- );
105
- const { listing100, listing33 } = await setupShop(this.client, setupData.gameserver.id);
106
-
107
- await this.client.playerOnGameserver.playerOnGameServerControllerAddCurrency(
108
- setupData.gameserver.id,
109
- setupData.players[0].id,
110
- { currency: 250 },
111
- );
112
-
113
- return { ...setupData, userClient, listing100, listing33 };
114
- };
4
+ const group = 'EconomyUtils:Shop:Claim';
115
5
 
116
6
  const tests = [
117
7
  new IntegrationTest<IShopSetup>({
118
8
  group,
119
9
  snapshot: false,
120
- setup: customSetup,
10
+ setup: shopSetup,
121
11
  name: 'Can claim an order happy path',
122
12
  test: async function () {
123
13
  await this.setupData.userClient.shopOrder.shopOrderControllerCreate({
@@ -137,7 +27,7 @@ const tests = [
137
27
  new IntegrationTest<IShopSetup>({
138
28
  group,
139
29
  snapshot: false,
140
- setup: customSetup,
30
+ setup: shopSetup,
141
31
  name: 'Shows a friendly error when there are no pending orders',
142
32
  test: async function () {
143
33
  const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 1);
@@ -153,7 +43,7 @@ const tests = [
153
43
  new IntegrationTest<IShopSetup>({
154
44
  group,
155
45
  snapshot: false,
156
- setup: customSetup,
46
+ setup: shopSetup,
157
47
  name: 'Handles double-claiming an order',
158
48
  test: async function () {
159
49
  await this.setupData.userClient.shopOrder.shopOrderControllerCreate({
@@ -182,7 +72,7 @@ const tests = [
182
72
  new IntegrationTest<IShopSetup>({
183
73
  group,
184
74
  snapshot: false,
185
- setup: customSetup,
75
+ setup: shopSetup,
186
76
  name: 'Only claims the first order by default',
187
77
  test: async function () {
188
78
  await this.setupData.userClient.shopOrder.shopOrderControllerCreate({
@@ -217,7 +107,7 @@ const tests = [
217
107
  new IntegrationTest<IShopSetup>({
218
108
  group,
219
109
  snapshot: false,
220
- setup: customSetup,
110
+ setup: shopSetup,
221
111
  name: 'Can claim all orders in one go',
222
112
  test: async function () {
223
113
  await this.setupData.userClient.shopOrder.shopOrderControllerCreate({
@@ -237,9 +127,9 @@ const tests = [
237
127
 
238
128
  expect(await events).to.have.length(4);
239
129
  expect((await events)[0].data.meta.msg).to.equal('You have received items from a shop order.');
240
- expect((await events)[1].data.meta.msg).to.equal('1x Wood');
130
+ expect((await events)[1].data.meta.msg).to.equal('1x Stone');
241
131
  expect((await events)[2].data.meta.msg).to.equal('You have received items from a shop order.');
242
- expect((await events)[3].data.meta.msg).to.equal('1x Stone');
132
+ expect((await events)[3].data.meta.msg).to.equal('1x Wood');
243
133
  },
244
134
  }),
245
135
  ];
@@ -6,9 +6,9 @@ import {
6
6
  chatMessageSorter,
7
7
  EventsAwaiter,
8
8
  } from '@takaro/test';
9
- import { GameEvents } from '../dto/index.js';
9
+ import { GameEvents } from '../../dto/index.js';
10
10
 
11
- const group = 'Economy utils suite';
11
+ const group = 'EconomyUtils';
12
12
 
13
13
  const customSetup = async function (this: IntegrationTest<IModuleTestsSetupData>): Promise<IModuleTestsSetupData> {
14
14
  const setupData = await modulesTestSetup.bind(this)();
@@ -0,0 +1,153 @@
1
+ import { EventsAwaiter, expect, IntegrationTest, IShopSetup, shopSetup } from '@takaro/test';
2
+ import { HookEvents } from '../../dto/index.js';
3
+
4
+ const group = 'EconomyUtils:Shop:Browse';
5
+
6
+ const tests = [
7
+ new IntegrationTest<IShopSetup>({
8
+ group,
9
+ snapshot: false,
10
+ setup: shopSetup,
11
+ name: 'Calling /shop without arguments displays help information',
12
+ test: async function () {
13
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 5);
14
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
15
+ msg: '/shop',
16
+ playerId: this.setupData.players[0].id,
17
+ });
18
+
19
+ expect(await events).to.have.length(5);
20
+ expect((await events)[0].data.meta.msg).to.eq(
21
+ 'This command allows you to browse the shop and view available items.',
22
+ );
23
+ expect((await events)[1].data.meta.msg).to.eq('Usage: /shop [page] [item] [action]');
24
+ expect((await events)[2].data.meta.msg).to.eq('/shop 2 - View the second page of shop items');
25
+ expect((await events)[3].data.meta.msg).to.eq('/shop 1 3 - View details about the third item on the first page');
26
+ expect((await events)[4].data.meta.msg).to.eq('/shop 1 3 buy - Purchase the third item on the first page');
27
+ },
28
+ }),
29
+ new IntegrationTest<IShopSetup>({
30
+ group,
31
+ snapshot: false,
32
+ setup: shopSetup,
33
+ name: 'First page shows the first 5 items',
34
+ test: async function () {
35
+ await this.setupData.createListings(this.client, { gameServerId: this.setupData.gameserver.id, amount: 5 });
36
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 5);
37
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
38
+ msg: '/shop 1',
39
+ playerId: this.setupData.players[0].id,
40
+ });
41
+
42
+ console.log(JSON.stringify((await events)[0].data.meta.msg, null, 2));
43
+ expect(await events).to.have.length(5);
44
+ },
45
+ }),
46
+ new IntegrationTest<IShopSetup>({
47
+ group,
48
+ snapshot: false,
49
+ setup: shopSetup,
50
+ name: 'Can view the second page of shop items',
51
+ test: async function () {
52
+ await this.client.gameserver.gameServerControllerInstallModule(
53
+ this.setupData.gameserver.id,
54
+ this.setupData.utilsModule.id,
55
+ );
56
+ await this.setupData.createListings(this.client, { gameServerId: this.setupData.gameserver.id, amount: 5 });
57
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 3);
58
+ const commandExecutedEvent = (await new EventsAwaiter().connect(this.client)).waitForEvents(
59
+ HookEvents.COMMAND_EXECUTED,
60
+ 1,
61
+ );
62
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
63
+ msg: '/shop 2',
64
+ playerId: this.setupData.players[0].id,
65
+ });
66
+
67
+ await commandExecutedEvent;
68
+
69
+ // We trigger the ping command to make sure we can see the FULL output of the previous command
70
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
71
+ msg: '/ping',
72
+ playerId: this.setupData.players[0].id,
73
+ });
74
+
75
+ expect(await events).to.have.length(3);
76
+ expect((await events)[(await events).length - 1].data.meta.msg).to.eq('Pong!');
77
+ },
78
+ }),
79
+ new IntegrationTest<IShopSetup>({
80
+ group,
81
+ snapshot: false,
82
+ setup: shopSetup,
83
+ name: 'When no items are available, shows a message',
84
+ test: async function () {
85
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 1);
86
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
87
+ msg: '/shop 5',
88
+ playerId: this.setupData.players[0].id,
89
+ });
90
+
91
+ expect(await events).to.have.length(1);
92
+ expect((await events)[0].data.meta.msg).to.equal('No items found.');
93
+ },
94
+ }),
95
+ new IntegrationTest<IShopSetup>({
96
+ group,
97
+ snapshot: false,
98
+ setup: shopSetup,
99
+ name: 'Can show details about an item',
100
+ test: async function () {
101
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 2);
102
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
103
+ msg: '/shop 1 1',
104
+ playerId: this.setupData.players[0].id,
105
+ });
106
+
107
+ expect(await events).to.have.length(2);
108
+ expect((await events)[0].data.meta.msg).to.equal('Listing Test item - 100 test coin');
109
+ expect((await events)[1].data.meta.msg).to.equal('- 1x Stone. Description: Stone can get you stoned');
110
+ },
111
+ }),
112
+ new IntegrationTest<IShopSetup>({
113
+ group,
114
+ snapshot: false,
115
+ setup: shopSetup,
116
+ name: 'Shows an error when requesting details for an invalid item',
117
+ test: async function () {
118
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 1);
119
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
120
+ msg: '/shop 1 5',
121
+ playerId: this.setupData.players[0].id,
122
+ });
123
+
124
+ expect(await events).to.have.length(1);
125
+ expect((await events)[0].data.meta.msg).to.equal(
126
+ 'Item not found. Please select an item from the list, valid options are 1-2.',
127
+ );
128
+ },
129
+ }),
130
+ new IntegrationTest<IShopSetup>({
131
+ group,
132
+ snapshot: false,
133
+ setup: shopSetup,
134
+ name: 'Can buy an item',
135
+ test: async function () {
136
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 2);
137
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
138
+ msg: '/shop 1 1 buy',
139
+ playerId: this.setupData.players[0].id,
140
+ });
141
+
142
+ expect(await events).to.have.length(2);
143
+ expect((await events)[0].data.meta.msg).to.equal('You have purchased Test item for 100 test coin.');
144
+ expect((await events)[1].data.meta.msg).to.equal('You have received items from a shop order.');
145
+ },
146
+ }),
147
+ ];
148
+
149
+ describe(group, function () {
150
+ tests.forEach((test) => {
151
+ test.run();
152
+ });
153
+ });
@@ -1,6 +1,5 @@
1
1
  import { IntegrationTest, expect, IModuleTestsSetupData, modulesTestSetup, EventsAwaiter } from '@takaro/test';
2
2
  import { GameEvents } from '../dto/index.js';
3
- import { sleep } from '@takaro/util';
4
3
 
5
4
  const group = 'Server messages';
6
5
 
@@ -77,24 +76,39 @@ const tests = [
77
76
 
78
77
  // We should see each of our test messages at least once
79
78
 
80
- const numberOfEvents = 10;
81
- const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(
82
- GameEvents.CHAT_MESSAGE,
83
- numberOfEvents,
84
- );
79
+ const firstEvents = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
80
+
81
+ await this.client.cronjob.cronJobControllerTrigger({
82
+ cronjobId: this.setupData.serverMessagesModule.cronJobs[0].id,
83
+ gameServerId: this.setupData.gameserver.id,
84
+ moduleId: this.setupData.serverMessagesModule.id,
85
+ });
86
+
87
+ expect((await firstEvents).length).to.be.eq(1);
88
+ expect((await firstEvents)[0].data.meta.msg).to.be.eq('Test message 1');
89
+
90
+ const secondEvents = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
91
+
92
+ await this.client.cronjob.cronJobControllerTrigger({
93
+ cronjobId: this.setupData.serverMessagesModule.cronJobs[0].id,
94
+ gameServerId: this.setupData.gameserver.id,
95
+ moduleId: this.setupData.serverMessagesModule.id,
96
+ });
97
+
98
+ expect((await secondEvents).length).to.be.eq(1);
99
+ expect((await secondEvents)[0].data.meta.msg).to.be.eq('Test message 2');
100
+
101
+ // After this, it should loop back to the first message
102
+ const thirdEvents = (await new EventsAwaiter().connect(this.client)).waitForEvents(GameEvents.CHAT_MESSAGE, 1);
103
+
104
+ await this.client.cronjob.cronJobControllerTrigger({
105
+ cronjobId: this.setupData.serverMessagesModule.cronJobs[0].id,
106
+ gameServerId: this.setupData.gameserver.id,
107
+ moduleId: this.setupData.serverMessagesModule.id,
108
+ });
85
109
 
86
- for (let i = 0; i < numberOfEvents; i++) {
87
- await sleep(Math.floor(Math.random() * 10) + 1);
88
- await this.client.cronjob.cronJobControllerTrigger({
89
- cronjobId: this.setupData.serverMessagesModule.cronJobs[0].id,
90
- gameServerId: this.setupData.gameserver.id,
91
- moduleId: this.setupData.serverMessagesModule.id,
92
- });
93
- }
94
-
95
- const messages = (await events).map((e) => e.data.meta.msg);
96
- expect(messages).to.include('Test message 1');
97
- expect(messages).to.include('Test message 2');
110
+ expect((await thirdEvents).length).to.be.eq(1);
111
+ expect((await thirdEvents)[0].data.meta.msg).to.be.eq('Test message 1');
98
112
  },
99
113
  }),
100
114
  ];
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  IsBoolean,
3
- IsDefined,
4
3
  IsEnum,
5
4
  IsIP,
6
5
  IsISO31661Alpha2,
@@ -141,9 +140,6 @@ export class TakaroEventCommandExecuted extends BaseEvent<TakaroEventCommandExec
141
140
  @IsString()
142
141
  type = TakaroEvents.COMMAND_EXECUTED;
143
142
 
144
- @IsDefined()
145
- data: Record<string, unknown>;
146
-
147
143
  @ValidateNested()
148
144
  @Type(() => TakaroEventFunctionResult)
149
145
  result: TakaroEventFunctionResult;
@@ -158,9 +154,6 @@ export class TakaroEventHookExecuted extends BaseEvent<TakaroEventHookExecuted>
158
154
  @IsString()
159
155
  type = TakaroEvents.HOOK_EXECUTED;
160
156
 
161
- @IsDefined()
162
- data: Record<string, unknown>;
163
-
164
157
  @ValidateNested()
165
158
  @Type(() => TakaroEventFunctionResult)
166
159
  result: TakaroEventFunctionResult;
@@ -175,9 +168,6 @@ export class TakaroEventCronjobExecuted extends BaseEvent<TakaroEventCronjobExec
175
168
  @IsString()
176
169
  type = TakaroEvents.CRONJOB_EXECUTED;
177
170
 
178
- @IsDefined()
179
- data: Record<string, unknown>;
180
-
181
171
  @ValidateNested()
182
172
  @Type(() => TakaroEventFunctionResult)
183
173
  result: TakaroEventFunctionResult;
package/src/main.ts CHANGED
@@ -8,6 +8,7 @@ import { Lottery } from './modules/lottery/index.js';
8
8
  import { PlayerOnboarding } from './modules/playerOnboarding/index.js';
9
9
  import { ServerMessages } from './modules/serverMessages/index.js';
10
10
  import { Teleports } from './modules/teleports/index.js';
11
+ import { TimedShutdown } from './modules/timedShutdown/index.js';
11
12
  import { Utils } from './modules/utils/index.js';
12
13
 
13
14
  export { BuiltinModule, ICommand, ICommandArgument, ICronJob, IHook, IFunction } from './BuiltinModule.js';
@@ -28,6 +29,7 @@ export function getModules(): Array<BuiltinModule<unknown>> {
28
29
  new EconomyUtils(),
29
30
  new Lottery(),
30
31
  new GeoBlock(),
32
+ new TimedShutdown(),
31
33
  ];
32
34
  }
33
35