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.
- package/lib/core/core.d.ts +15 -0
- package/lib/core/core.js +25 -8
- package/lib/core/ddutils.d.ts +3 -0
- package/lib/core/ddutils.js +3 -0
- package/lib/core/module.d.ts +3 -3
- package/lib/modules/chat.d.ts +1 -2
- package/lib/modules/chat.js +15 -22
- package/lib/modules/playerlist.d.ts +1 -1
- package/lib/modules/reconnect.d.ts +9 -1
- package/lib/modules/reconnect.js +7 -3
- package/lib/modules/snap.d.ts +3 -0
- package/lib/modules/snap.js +22 -1
- package/package.json +5 -2
package/lib/core/core.d.ts
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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));
|
package/lib/core/ddutils.d.ts
CHANGED
|
@@ -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;
|
package/lib/core/ddutils.js
CHANGED
package/lib/core/module.d.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
26
|
+
protected _start(...args: TStartArgs): void;
|
|
27
27
|
/**
|
|
28
28
|
* Метод, который нужно переопределить в наследниках
|
|
29
29
|
* Здесь происходит очистка при остановке
|
package/lib/modules/chat.d.ts
CHANGED
|
@@ -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;
|
package/lib/modules/chat.js
CHANGED
|
@@ -8,26 +8,21 @@ class Chat extends BaseModule {
|
|
|
8
8
|
lastSentTime = 0;
|
|
9
9
|
cooldown = 1000;
|
|
10
10
|
chatlistener = (msg) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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;
|
package/lib/modules/reconnect.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/lib/modules/snap.d.ts
CHANGED
|
@@ -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
|
}
|
package/lib/modules/snap.js
CHANGED
|
@@ -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.
|
|
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"
|