ddbot.js-0374 4.0.0 → 4.2.0

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.
@@ -2,6 +2,7 @@ import { Client } from 'teeworlds';
2
2
  import { EventEmitter } from 'events';
3
3
  import * as Types from '../types.js';
4
4
  interface BotEvents {
5
+ error: (...args: any[]) => void;
5
6
  connect: (info: Types.ConnectionInfo) => void;
6
7
  disconnect: (reason: string | null, info: Types.ConnectionInfo) => void;
7
8
  broadcast: (message: string) => void;
@@ -16,6 +17,13 @@ interface BotEvents {
16
17
  kill: (kill: Types.SnapshotItemTypes.iKillMsg) => void;
17
18
  snapshot: (items: Types.DeltaItem[]) => void;
18
19
  map_change: (message: Types.SnapshotItemTypes.iMapChange) => void;
20
+ map_details: (message: {
21
+ map_name: string;
22
+ map_sha256: Buffer;
23
+ map_crc: number;
24
+ map_size: number;
25
+ map_url: string;
26
+ }) => void;
19
27
  motd: (message: string) => void;
20
28
  message: (message: Types.SnapshotItemTypes.iMessage) => void;
21
29
  teams: (teams: Array<number>) => void;
@@ -44,7 +52,14 @@ export declare class Bot extends EventEmitter {
44
52
  options: Types.SnapshotItemTypes.iOptions;
45
53
  identity: Types.SnapshotItemTypes.Identity;
46
54
  status: BotStatus;
55
+ /**
56
+ *
57
+ * @param identity Bot identity
58
+ * @param options Options for teeworlds.Client
59
+ * @param CustomTeeworlds Custom teeworlds
60
+ */
47
61
  constructor(identity?: Types.SnapshotItemTypes.Identity, options?: Types.SnapshotItemTypes.iOptions, CustomTeeworlds?: typeof import('teeworlds'));
62
+ private error;
48
63
  /**
49
64
  * Get bot identity
50
65
  */
package/lib/core/core.js CHANGED
@@ -18,6 +18,12 @@ export class Bot extends EventEmitter {
18
18
  addr: null,
19
19
  port: null,
20
20
  };
21
+ /**
22
+ *
23
+ * @param identity Bot identity
24
+ * @param options Options for teeworlds.Client
25
+ * @param CustomTeeworlds Custom teeworlds
26
+ */
21
27
  constructor(identity, options = {}, CustomTeeworlds = Teeworlds) {
22
28
  super();
23
29
  this.teeworlds = CustomTeeworlds;
@@ -26,6 +32,10 @@ export class Bot extends EventEmitter {
26
32
  ? identity
27
33
  : DDUtils.DefaultIdentity('nameless tee');
28
34
  }
35
+ error(...err) {
36
+ //console.error(err);
37
+ this.emit('error', ...err);
38
+ }
29
39
  /**
30
40
  * Get bot identity
31
41
  */
@@ -35,8 +45,9 @@ export class Bot extends EventEmitter {
35
45
  /**
36
46
  * Create new Teeworlds client instance
37
47
  */
38
- create_client(addr, port) {
39
- this.disconnect();
48
+ async create_client(addr, port) {
49
+ if (this.client)
50
+ await this.client.Disconnect();
40
51
  this.clean(true);
41
52
  this.client = new this.teeworlds.Client(addr, port, this.identity.name, {
42
53
  ...this.options,
@@ -59,7 +70,7 @@ export class Bot extends EventEmitter {
59
70
  }
60
71
  }
61
72
  catch (e) {
62
- console.error('Error during clean:', e);
73
+ this.error('Error during clean:', e);
63
74
  }
64
75
  }
65
76
  /**
@@ -69,7 +80,7 @@ export class Bot extends EventEmitter {
69
80
  * @param timeout - Connection timeout in ms (default: 5000)
70
81
  * @returns Promise with connection info
71
82
  */
72
- connect(addr, port = 8303, timeout = 5000) {
83
+ async connect(addr, port = 8303, timeout = 5000) {
73
84
  if (typeof addr !== 'string' || typeof port !== 'number') {
74
85
  return Promise.reject(new Error('Invalid address or port'));
75
86
  }
@@ -82,8 +93,8 @@ export class Bot extends EventEmitter {
82
93
  this.status.connect.connecting = true;
83
94
  this.status.addr = addr;
84
95
  this.status.port = port;
96
+ await this.create_client(addr, port);
85
97
  return new Promise((resolve, reject) => {
86
- this.create_client(addr, port);
87
98
  let settled = false;
88
99
  const cleanup = () => {
89
100
  this.off('connect', onConnect);
@@ -118,6 +129,10 @@ export class Bot extends EventEmitter {
118
129
  }, timeout);
119
130
  this.once('connect', onConnect);
120
131
  this.once('disconnect', onDisconnect);
132
+ if (!this.client) {
133
+ reject(new Error('Client not created'));
134
+ return;
135
+ }
121
136
  this.client.connect();
122
137
  });
123
138
  }
@@ -133,7 +148,7 @@ export class Bot extends EventEmitter {
133
148
  await this.client.Disconnect();
134
149
  }
135
150
  catch (e) {
136
- console.error('Error during disconnect:', e);
151
+ this.error('Error during disconnect:', e);
137
152
  }
138
153
  this.status.connect.connected = false;
139
154
  info = { addr: this.status.addr, port: this.status.port };
@@ -180,7 +195,7 @@ export class Bot extends EventEmitter {
180
195
  this.client.on('disconnect', (reason = null) => {
181
196
  this.status.connect.connected = false;
182
197
  this.clean(true);
183
- if (reason) {
198
+ if (!!reason) {
184
199
  this.emit('disconnect', reason, { addr: this.status.addr, port: this.status.port });
185
200
  }
186
201
  });
@@ -191,6 +206,7 @@ export class Bot extends EventEmitter {
191
206
  this.client.on('kill', (msg) => this.emit('kill', msg));
192
207
  this.client.on('snapshot', (msg) => this.emit('snapshot', msg));
193
208
  this.client.on('map_change', (msg) => this.emit('map_change', msg));
209
+ this.client.on('map_details', (msg) => this.emit('map_details', msg));
194
210
  this.client.on('motd', (msg) => this.emit('motd', msg));
195
211
  this.client.on('message', (msg) => this.emit('message', msg));
196
212
  this.client.on('teams', (msg) => this.emit('teams', msg));
@@ -201,9 +217,10 @@ export class Bot extends EventEmitter {
201
217
  */
202
218
  setup_snapshot_events() {
203
219
  if (!this.client?.SnapshotUnpacker) {
204
- console.warn('SnapshotUnpacker not available yet');
220
+ this.error('SnapshotUnpacker not available yet');
205
221
  return;
206
222
  }
223
+ this.client.SnapshotUnpacker.removeAllListeners();
207
224
  this.client.SnapshotUnpacker.on('spawn', (msg) => this.emit('spawn', msg));
208
225
  this.client.SnapshotUnpacker.on('death', (msg) => this.emit('death', msg));
209
226
  this.client.SnapshotUnpacker.on('hammerhit', (msg) => this.emit('hammerhit', msg));
@@ -30,4 +30,7 @@ export declare function IsValidInput(input: unknown): input is Input;
30
30
  export declare function random(min: number, max: number): number;
31
31
  export declare function connectionInfo(): ConnectionInfo;
32
32
  import type { SnapshotItemTypes } from '../types.js';
33
+ /**
34
+ * NOT FULL. baze.
35
+ */
33
36
  export declare function reconstructPlayerInput(char: SnapshotItemTypes.Character, ddnetChar?: SnapshotItemTypes.DDNetCharacter | null, tick?: number | null): SnapshotItemTypes.PlayerInput;
@@ -109,6 +109,9 @@ export function connectionInfo() {
109
109
  port: 8303
110
110
  };
111
111
  }
112
+ /**
113
+ * NOT FULL. baze.
114
+ */
112
115
  export function reconstructPlayerInput(char, ddnetChar = null, tick = null) {
113
116
  const input = {
114
117
  direction: char.character_core.direction,
@@ -4,7 +4,7 @@ interface BaseModuleOptions {
4
4
  moduleName?: string;
5
5
  offonDisconnect?: boolean;
6
6
  }
7
- declare class BaseModule extends EventEmitter {
7
+ declare class BaseModule<TStartArgs extends unknown[] = []> extends EventEmitter {
8
8
  protected readonly bot: Bot;
9
9
  readonly moduleName: string;
10
10
  isRunning: boolean;
@@ -14,7 +14,7 @@ declare class BaseModule extends EventEmitter {
14
14
  * Запускает модуль, если он ещё не запущен
15
15
  * @param args — аргументы, которые будут переданы в _start
16
16
  */
17
- start(...args: unknown[]): void;
17
+ start(...args: TStartArgs): void;
18
18
  /**
19
19
  * Останавливает модуль, если он запущен
20
20
  */
@@ -23,7 +23,7 @@ declare class BaseModule extends EventEmitter {
23
23
  * Метод, который нужно переопределить в наследниках
24
24
  * Здесь происходит основная логика запуска
25
25
  */
26
- protected _start(...args: unknown[]): void;
26
+ protected _start(...args: TStartArgs): void;
27
27
  /**
28
28
  * Метод, который нужно переопределить в наследниках
29
29
  * Здесь происходит очистка при остановке
@@ -16,7 +16,7 @@ interface ChatEvents {
16
16
  queueSize: number;
17
17
  }) => void;
18
18
  }
19
- declare class Chat extends BaseModule {
19
+ declare class Chat extends BaseModule<[interval?: number, cooldown?: number]> {
20
20
  private chatinterval;
21
21
  private sendinterval;
22
22
  private readonly chatset;
@@ -37,7 +37,6 @@ declare class Chat extends BaseModule {
37
37
  private _processQueue;
38
38
  protected _start(interval?: number, cooldown?: number): void;
39
39
  protected _stop(): void;
40
- destroy(): void;
41
40
  on<K extends keyof ChatEvents>(event: K, listener: ChatEvents[K]): this;
42
41
  once<K extends keyof ChatEvents>(event: K, listener: ChatEvents[K]): this;
43
42
  emit<K extends keyof ChatEvents>(event: K, ...args: Parameters<ChatEvents[K]>): boolean;
@@ -8,26 +8,21 @@ class Chat extends BaseModule {
8
8
  lastSentTime = 0;
9
9
  cooldown = 1000;
10
10
  chatlistener = (msg) => {
11
- try {
12
- const msgraw = msg;
13
- const text = String(msgraw?.message ?? '');
14
- const client_id = msgraw.client_id ?? -1;
15
- const team = msgraw.team ?? 0;
16
- const autormsg = msgraw?.author?.ClientInfo?.name ?? null;
17
- const key = `${client_id}:${text}:${team}`;
18
- if (this.chatset.has(key))
19
- return;
20
- this.chatset.add(key);
21
- this.emit('anychat', msgraw, autormsg, text, team, client_id);
22
- if (autormsg) {
23
- this.emit('chat', msgraw, autormsg, text, team, client_id);
24
- }
25
- else {
26
- this.emit('systemchat', msgraw, text);
27
- }
11
+ const msgraw = msg;
12
+ const text = String(msgraw?.message ?? '');
13
+ const client_id = msgraw.client_id ?? -1;
14
+ const team = msgraw.team ?? 0;
15
+ const autormsg = msgraw?.author?.ClientInfo?.name ?? null;
16
+ const key = `${client_id}:${text}:${team}`;
17
+ if (this.chatset.has(key))
18
+ return;
19
+ this.chatset.add(key);
20
+ this.emit('anychat', msgraw, autormsg, text, team, client_id);
21
+ if (autormsg) {
22
+ this.emit('chat', msgraw, autormsg, text, team, client_id);
28
23
  }
29
- catch (e) {
30
- console.error(`[${this.moduleName}] chat listener error:`, e);
24
+ else {
25
+ this.emit('systemchat', msgraw, text);
31
26
  }
32
27
  };
33
28
  constructor(bot) {
@@ -92,6 +87,7 @@ class Chat extends BaseModule {
92
87
  _stop() {
93
88
  this.bot.off('message', this.chatlistener);
94
89
  this.chatset.clear();
90
+ this.queue.length = 0;
95
91
  if (this.chatinterval) {
96
92
  clearInterval(this.chatinterval);
97
93
  this.chatinterval = null;
@@ -101,9 +97,6 @@ class Chat extends BaseModule {
101
97
  this.sendinterval = null;
102
98
  }
103
99
  }
104
- destroy() {
105
- super.destroy();
106
- }
107
100
  on(event, listener) {
108
101
  return super.on(event, listener);
109
102
  }
@@ -8,7 +8,7 @@ interface PlayerData {
8
8
  character: Types.SnapshotItemTypes.Character | null;
9
9
  DDNetCharacter: Types.SnapshotItemTypes.DDNetCharacter | null;
10
10
  }
11
- declare class PlayerList extends BaseModule {
11
+ declare class PlayerList extends BaseModule<[maxclients?: number]> {
12
12
  constructor(bot: Bot);
13
13
  private client;
14
14
  private maxclients;
@@ -1,10 +1,18 @@
1
1
  import BaseModule from '../core/module.js';
2
2
  import type { Bot } from '../core/core.js';
3
- declare class Reconnect extends BaseModule {
3
+ import * as Types from '../types.js';
4
+ export interface ReconnectingInfo {
5
+ attempt: number;
6
+ delay: number;
7
+ reason: string | null;
8
+ ConnectionInfo: Types.ConnectionInfo;
9
+ }
10
+ declare class Reconnect extends BaseModule<[maxAttempts?: number, randomDelay?: boolean]> {
4
11
  private maxAttempts;
5
12
  private randomDelay;
6
13
  private currentAttempts;
7
14
  private reconnecting;
15
+ private reconnectTimer;
8
16
  constructor(bot: Bot);
9
17
  protected _start(maxAttempts?: number, randomDelay?: boolean): void;
10
18
  private handleDisconnect;
@@ -5,6 +5,7 @@ class Reconnect extends BaseModule {
5
5
  randomDelay = true;
6
6
  currentAttempts = 0;
7
7
  reconnecting = false;
8
+ reconnectTimer = null;
8
9
  constructor(bot) {
9
10
  super(bot, { moduleName: 'Reconnect', offonDisconnect: false });
10
11
  }
@@ -33,10 +34,9 @@ class Reconnect extends BaseModule {
33
34
  attempt: this.currentAttempts,
34
35
  delay,
35
36
  reason,
36
- addr: connectionInfo.addr,
37
- port: connectionInfo.port,
37
+ ConnectionInfo: connectionInfo
38
38
  });
39
- setTimeout(async () => {
39
+ this.reconnectTimer = setTimeout(async () => {
40
40
  try {
41
41
  await this.bot.connect(connectionInfo.addr, connectionInfo.port);
42
42
  this.currentAttempts = 0;
@@ -76,6 +76,10 @@ class Reconnect extends BaseModule {
76
76
  this.bot.off('disconnect', this.handleDisconnect);
77
77
  this.reconnecting = false;
78
78
  this.currentAttempts = 0;
79
+ if (this.reconnectTimer) {
80
+ clearTimeout(this.reconnectTimer);
81
+ this.reconnectTimer = null;
82
+ }
79
83
  }
80
84
  }
81
85
  export default Reconnect;
@@ -2,8 +2,10 @@ import BaseModule from '../core/module.js';
2
2
  import type { Bot } from '../core/core.js';
3
3
  import * as Types from '../types.js';
4
4
  declare class Snap extends BaseModule {
5
+ private _isFrozen;
5
6
  private readonly hammerHitlistener;
6
7
  private readonly firelistener;
8
+ private readonly snapslistener;
7
9
  constructor(bot: Bot);
8
10
  private static areWithinTile;
9
11
  private static whoareWithinTile;
@@ -11,6 +13,7 @@ declare class Snap extends BaseModule {
11
13
  x: number;
12
14
  y: number;
13
15
  } | null;
16
+ get isFrozen(): boolean;
14
17
  protected _start(): void;
15
18
  protected _stop(): void;
16
19
  }
@@ -1,6 +1,7 @@
1
1
  import BaseModule from '../core/module.js';
2
2
  import * as Types from '../types.js';
3
3
  class Snap extends BaseModule {
4
+ _isFrozen = false;
4
5
  hammerHitlistener = (hit) => {
5
6
  if (this.bot.OwnID === undefined)
6
7
  return;
@@ -17,6 +18,21 @@ class Snap extends BaseModule {
17
18
  this.emit('fire', { common: sound.common }, Snap.whoareWithinTile(sound.common.x, sound.common.y, list));
18
19
  }
19
20
  };
21
+ snapslistener = () => {
22
+ const ffs = () => {
23
+ if (this.bot.OwnID === undefined || !this.bot.bot_client?.SnapshotUnpacker)
24
+ return;
25
+ const myDDNetChar = this.bot.bot_client.SnapshotUnpacker.getObjExDDNetCharacter(this.bot.OwnID);
26
+ if (myDDNetChar) {
27
+ const wasFrozen = this._isFrozen;
28
+ this._isFrozen = myDDNetChar.m_FreezeEnd !== 0;
29
+ if (wasFrozen !== this._isFrozen) {
30
+ this.emit(this._isFrozen ? 'frozen' : 'unfrozen');
31
+ }
32
+ }
33
+ };
34
+ ffs(); // in future snapshot can be biger, so we just make new functions. блять идите нахуй со своим англиским, я не знаю но я стараюсь идите нахуй
35
+ };
20
36
  constructor(bot) {
21
37
  super(bot, { moduleName: 'Snap', offonDisconnect: false });
22
38
  }
@@ -26,7 +42,7 @@ class Snap extends BaseModule {
26
42
  const distanceY = Math.abs(y1 - y2);
27
43
  return distanceX <= TILE && distanceY <= TILE;
28
44
  }
29
- static whoareWithinTile(x, y, list, ignoreClients = []) {
45
+ static whoareWithinTile(x, y, list = [], ignoreClients = []) {
30
46
  for (const character of list) {
31
47
  const character_core = character?.character_core;
32
48
  if (!character_core)
@@ -49,11 +65,16 @@ class Snap extends BaseModule {
49
65
  y: Math.sin(angleRad) * 256,
50
66
  };
51
67
  }
68
+ get isFrozen() {
69
+ return this._isFrozen;
70
+ }
52
71
  _start() {
72
+ this.bot.on('snapshot', this.snapslistener);
53
73
  this.bot.on('hammerhit', this.hammerHitlistener);
54
74
  this.bot.on('sound_world', this.firelistener);
55
75
  }
56
76
  _stop() {
77
+ this.bot.off('snapshot', this.snapslistener);
57
78
  this.bot.off('hammerhit', this.hammerHitlistener);
58
79
  this.bot.off('sound_world', this.firelistener);
59
80
  }
package/package.json CHANGED
@@ -3,10 +3,13 @@
3
3
  "teeworlds": "^2.5.11"
4
4
  },
5
5
  "name": "ddbot.js-0374",
6
- "version": "4.0.0",
6
+ "version": "4.2.0",
7
7
  "description": "ddbot.js — это Node.js проект для автоматизации и управления ботами.",
8
8
  "main": "./index.js",
9
- "scripts": {},
9
+ "scripts": {
10
+ "build": "tsc && tsc index.ts --outDir . --skipLibCheck",
11
+ "prepublishOnly": "npm run build"
12
+ },
10
13
  "repository": {
11
14
  "type": "git",
12
15
  "url": "git+https://github.com/0374flop/ddbot.js.git"