kol.js 0.0.2 → 0.1.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/package.json +7 -6
- package/src/Cache.ts +33 -0
- package/src/Client.test.ts +206 -0
- package/src/Client.ts +499 -0
- package/src/Player.test.ts +101 -0
- package/src/Player.ts +209 -0
- package/src/__fixtures__/backoffice_prices_alien_meat.html +46 -0
- package/src/__fixtures__/backoffice_prices_lov_elephant.html +30 -0
- package/src/__fixtures__/backoffice_prices_magical_mystery_juice.html +30 -0
- package/src/__fixtures__/backoffice_prices_tofurkey_leg.html +48 -0
- package/src/__fixtures__/backoffice_prices_turtle_wax_greaves.html +42 -0
- package/src/__fixtures__/backoffice_prices_turtle_wax_helmet.html +42 -0
- package/src/__fixtures__/backoffice_prices_turtle_wax_shield.html +47 -0
- package/src/__fixtures__/desc_effect_pasta_oneness.html +31 -0
- package/src/__fixtures__/desc_effect_the_visible_adventurer.html +31 -0
- package/src/__fixtures__/desc_item_alien_meat.html +38 -0
- package/src/__fixtures__/desc_item_hypodermic_needle.html +39 -0
- package/src/__fixtures__/desc_item_lov_elephant.html +39 -0
- package/src/__fixtures__/desc_item_magical_mystery_juice.html +39 -0
- package/src/__fixtures__/desc_item_mosquito_larva.html +39 -0
- package/src/__fixtures__/desc_item_tofurkey_leg.html +38 -0
- package/src/__fixtures__/desc_item_turtle_wax_shield.html +79 -0
- package/src/__fixtures__/desc_skill_impetuous_sauciness.html +32 -0
- package/src/__fixtures__/desc_skill_overload_discarded_refridgerator.html +32 -0
- package/src/__fixtures__/familiar_in_standard_run.html +184 -0
- package/src/__fixtures__/searchplayer_mad_carew.html +53 -0
- package/src/__fixtures__/showplayer_dependence_day.html +47 -0
- package/src/__fixtures__/showplayer_golden_gun.html +28 -0
- package/src/__fixtures__/showplayer_regular.html +56 -0
- package/{dist/index.d.ts → src/index.ts} +1 -0
- package/src/testUtils.ts +13 -0
- package/src/utils/avatar.ts +90 -0
- package/src/utils/kmail.ts +48 -0
- package/src/utils/leaderboard.ts +84 -0
- package/src/utils/utils.ts +33 -0
- package/dist/Cache.d.ts +0 -11
- package/dist/Cache.js +0 -26
- package/dist/Client.d.ts +0 -84
- package/dist/Client.js +0 -343
- package/dist/Client.test.d.ts +0 -1
- package/dist/Client.test.js +0 -162
- package/dist/Player.d.ts +0 -28
- package/dist/Player.js +0 -136
- package/dist/Player.test.d.ts +0 -1
- package/dist/Player.test.js +0 -48
- package/dist/index.js +0 -3
- package/dist/testUtils.d.ts +0 -2
- package/dist/testUtils.js +0 -10
- package/dist/utils/avatar.d.ts +0 -1
- package/dist/utils/avatar.js +0 -70
- package/dist/utils/kmail.d.ts +0 -32
- package/dist/utils/kmail.js +0 -1
- package/dist/utils/leaderboard.d.ts +0 -16
- package/dist/utils/leaderboard.js +0 -56
- package/dist/utils/utils.d.ts +0 -4
- package/dist/utils/utils.js +0 -27
- package/dist/utils/visit.d.ts +0 -3
- package/dist/utils/visit.js +0 -10
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Player } from "../Player.js";
|
|
2
|
+
|
|
3
|
+
export type KoLChatMessage = {
|
|
4
|
+
who?: Player<false>;
|
|
5
|
+
type?: string;
|
|
6
|
+
msg?: string;
|
|
7
|
+
link?: string;
|
|
8
|
+
channel?: string;
|
|
9
|
+
time: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type KoLMessageType = "private" | "system" | "public" | "kmail";
|
|
13
|
+
|
|
14
|
+
export const isValidMessage = (
|
|
15
|
+
msg: KoLChatMessage,
|
|
16
|
+
): msg is KoLChatMessage & {
|
|
17
|
+
type: KoLMessageType;
|
|
18
|
+
who: Player<false>;
|
|
19
|
+
msg: string;
|
|
20
|
+
} => msg.who !== undefined && msg.msg !== undefined;
|
|
21
|
+
|
|
22
|
+
export type KoLKmail = {
|
|
23
|
+
id: string;
|
|
24
|
+
type: string;
|
|
25
|
+
fromid: string;
|
|
26
|
+
fromname: string;
|
|
27
|
+
azunixtime: string;
|
|
28
|
+
message: string;
|
|
29
|
+
localtime: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type BaseKoLMessage = {
|
|
33
|
+
type: KoLMessageType;
|
|
34
|
+
who: Player<false>;
|
|
35
|
+
msg: string;
|
|
36
|
+
time: Date;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export interface KmailMessage extends BaseKoLMessage {
|
|
40
|
+
type: "kmail";
|
|
41
|
+
id: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ChatMessage extends BaseKoLMessage {
|
|
45
|
+
type: "public" | "private" | "system";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type KoLMessage = KmailMessage | ChatMessage;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import xpath, { select } from "xpath";
|
|
2
|
+
import { DOMParser } from "@xmldom/xmldom";
|
|
3
|
+
|
|
4
|
+
export type LeaderboardInfo = {
|
|
5
|
+
name: string;
|
|
6
|
+
boards: SubboardInfo[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type SubboardInfo = {
|
|
10
|
+
name: string;
|
|
11
|
+
runs: RunInfo[];
|
|
12
|
+
updated: Date | null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type RunInfo = {
|
|
16
|
+
player: string;
|
|
17
|
+
days: string;
|
|
18
|
+
turns: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const parser = new DOMParser({
|
|
22
|
+
locator: {},
|
|
23
|
+
errorHandler: {
|
|
24
|
+
warning: () => {},
|
|
25
|
+
error: () => {},
|
|
26
|
+
fatalError: console.error,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const selectMulti = (expression: string, node: Node) => {
|
|
31
|
+
const selection = select(expression, node);
|
|
32
|
+
if (Array.isArray(selection)) return selection;
|
|
33
|
+
return selection instanceof Node ? [selection] : [];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function parseLeaderboard(page: string): LeaderboardInfo {
|
|
37
|
+
const document = parser.parseFromString(page);
|
|
38
|
+
const [board, ...boards] = selectMulti("//table", document);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
name: selectMulti(".//text()", board.firstChild!)
|
|
42
|
+
.map((node) => node.nodeValue)
|
|
43
|
+
.join("")
|
|
44
|
+
.replace(/\s+/g, " ")
|
|
45
|
+
.trim(),
|
|
46
|
+
boards: boards
|
|
47
|
+
.slice(1)
|
|
48
|
+
.filter(
|
|
49
|
+
(board) =>
|
|
50
|
+
selectMulti("./tr//text()", board)[0]?.nodeValue?.match(
|
|
51
|
+
/^((Fast|Funn|B)est|Most (Goo|Elf))/,
|
|
52
|
+
) && selectMulti("./tr", board).length > 1,
|
|
53
|
+
)
|
|
54
|
+
.map((subboard) => {
|
|
55
|
+
const rows = selectMulti("./tr", subboard);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
name: (selectMulti(".//text()", rows[0])[0]?.nodeValue || "").trim(),
|
|
59
|
+
runs: selectMulti("./td//tr", rows[1])
|
|
60
|
+
.slice(2)
|
|
61
|
+
.map((node) => {
|
|
62
|
+
const rowText = selectMulti(".//text()", node).map((text) =>
|
|
63
|
+
text.toString().replace(/&nbsp;/g, ""),
|
|
64
|
+
);
|
|
65
|
+
const hasTwoNumbers = !!parseInt(rowText[rowText.length - 2]);
|
|
66
|
+
return {
|
|
67
|
+
player: rowText
|
|
68
|
+
.slice(0, rowText.length - (hasTwoNumbers ? 2 : 1))
|
|
69
|
+
.join("")
|
|
70
|
+
.trim()
|
|
71
|
+
.toString(),
|
|
72
|
+
days: hasTwoNumbers
|
|
73
|
+
? rowText[rowText.length - 2].toString() || "0"
|
|
74
|
+
: "",
|
|
75
|
+
turns: rowText[rowText.length - 1].toString() || "0",
|
|
76
|
+
};
|
|
77
|
+
}),
|
|
78
|
+
updated: xpath.isComment(subboard.nextSibling)
|
|
79
|
+
? new Date(subboard.nextSibling.data.slice(9, -1))
|
|
80
|
+
: null,
|
|
81
|
+
};
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parse as parseDate } from "date-fns";
|
|
2
|
+
import { decode } from "html-entities";
|
|
3
|
+
|
|
4
|
+
export function parsePlayerDate(input?: string) {
|
|
5
|
+
if (!input) return new Date();
|
|
6
|
+
return parseDate(input, "MMMM dd, yyyy", new Date());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function sanitiseBlueText(blueText: string | undefined): string {
|
|
10
|
+
if (!blueText) return "";
|
|
11
|
+
return decode(
|
|
12
|
+
blueText
|
|
13
|
+
.replace(/\r/g, "")
|
|
14
|
+
.replace(/\r/g, "")
|
|
15
|
+
.replace(/(<p><\/p>)|(<br>)|(<Br>)|(<br \/>)|(<Br \/>)/g, "\n")
|
|
16
|
+
.replace(/<[^<>]+>/g, "")
|
|
17
|
+
.replace(/(\n+)/g, "\n")
|
|
18
|
+
.replace(/(\n)+$/, ""),
|
|
19
|
+
).trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function resolveKoLImage(path: string) {
|
|
23
|
+
if (!/^https?:\/\//i.test(path))
|
|
24
|
+
return (
|
|
25
|
+
"https://s3.amazonaws.com/images.kingdomofloathing.com" +
|
|
26
|
+
path.replace(/^\/(iii|images)/, "")
|
|
27
|
+
);
|
|
28
|
+
return path;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function wait(ms: number) {
|
|
32
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
33
|
+
}
|
package/dist/Cache.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Client } from "./Client.js";
|
|
2
|
-
import { Player } from "./Player.js";
|
|
3
|
-
export declare class Cache<T> {
|
|
4
|
-
client: Client;
|
|
5
|
-
constructor(client: Client);
|
|
6
|
-
cache: T[];
|
|
7
|
-
}
|
|
8
|
-
export declare class PlayerCache extends Cache<Player<boolean>> {
|
|
9
|
-
fetch(identifier: string | number, full: true): Promise<Player<true>>;
|
|
10
|
-
fetch(identifier: string | number): Promise<Player<boolean>>;
|
|
11
|
-
}
|
package/dist/Cache.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Player } from "./Player.js";
|
|
2
|
-
export class Cache {
|
|
3
|
-
client;
|
|
4
|
-
constructor(client) {
|
|
5
|
-
this.client = client;
|
|
6
|
-
}
|
|
7
|
-
cache = [];
|
|
8
|
-
}
|
|
9
|
-
export class PlayerCache extends Cache {
|
|
10
|
-
async fetch(identifier, full = false) {
|
|
11
|
-
const player = await (async () => {
|
|
12
|
-
const cached = this.cache.find((p) => p.matchesIdentifier(identifier));
|
|
13
|
-
if (cached) {
|
|
14
|
-
return cached;
|
|
15
|
-
}
|
|
16
|
-
const fetched = await Player.from(this.client, identifier);
|
|
17
|
-
if (!fetched)
|
|
18
|
-
return null;
|
|
19
|
-
this.cache.push(fetched);
|
|
20
|
-
return fetched;
|
|
21
|
-
})();
|
|
22
|
-
if (!player)
|
|
23
|
-
return player;
|
|
24
|
-
return full ? await player.full() : player;
|
|
25
|
-
}
|
|
26
|
-
}
|
package/dist/Client.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Mutex } from "async-mutex";
|
|
2
|
-
import TypedEventEmitter, { EventMap } from "typed-emitter";
|
|
3
|
-
import { KoLMessage } from "./utils/kmail.js";
|
|
4
|
-
import { PlayerCache } from "./Cache.js";
|
|
5
|
-
type TypedEmitter<T extends EventMap> = TypedEventEmitter.default<T>;
|
|
6
|
-
type FetchOptions<Result> = {
|
|
7
|
-
params?: Record<string, string | number>;
|
|
8
|
-
body?: Record<string, string | number>;
|
|
9
|
-
method?: "POST" | "GET";
|
|
10
|
-
fallback?: Result;
|
|
11
|
-
};
|
|
12
|
-
type MallPrice = {
|
|
13
|
-
formattedMallPrice: string;
|
|
14
|
-
formattedLimitedMallPrice: string;
|
|
15
|
-
formattedMinPrice: string;
|
|
16
|
-
mallPrice: number;
|
|
17
|
-
limitedMallPrice: number;
|
|
18
|
-
minPrice: number | null;
|
|
19
|
-
};
|
|
20
|
-
type Events = {
|
|
21
|
-
kmail: (message: KoLMessage) => void;
|
|
22
|
-
whisper: (message: KoLMessage) => void;
|
|
23
|
-
system: (message: KoLMessage) => void;
|
|
24
|
-
public: (message: KoLMessage) => void;
|
|
25
|
-
rollover: () => void;
|
|
26
|
-
};
|
|
27
|
-
type Familiar = {
|
|
28
|
-
id: number;
|
|
29
|
-
name: string;
|
|
30
|
-
image: string;
|
|
31
|
-
};
|
|
32
|
-
declare const Client_base: new () => TypedEmitter<Events>;
|
|
33
|
-
export declare class Client extends Client_base {
|
|
34
|
-
#private;
|
|
35
|
-
static loginMutex: Mutex;
|
|
36
|
-
actionMutex: Mutex;
|
|
37
|
-
session: import("fetch-cookie").FetchCookieImpl<string | URL | Request, RequestInit, Response>;
|
|
38
|
-
players: PlayerCache;
|
|
39
|
-
private lastFetchedMessages;
|
|
40
|
-
private postRolloverLatch;
|
|
41
|
-
constructor(username: string, password: string);
|
|
42
|
-
fetchText(path: string, options?: FetchOptions<string>): Promise<string>;
|
|
43
|
-
fetchJson<Result>(path: string, options?: FetchOptions<Result>): Promise<Result | null>;
|
|
44
|
-
login(): Promise<boolean>;
|
|
45
|
-
isRollover(): boolean;
|
|
46
|
-
checkLoggedIn(): Promise<boolean>;
|
|
47
|
-
startChatBot(): Promise<void>;
|
|
48
|
-
private loopChatBot;
|
|
49
|
-
checkMessages(): Promise<void>;
|
|
50
|
-
checkKmails(): Promise<void>;
|
|
51
|
-
sendChat(message: string): Promise<{
|
|
52
|
-
output: string;
|
|
53
|
-
msgs: string[];
|
|
54
|
-
} | null>;
|
|
55
|
-
useChatMacro(macro: string): Promise<{
|
|
56
|
-
output: string;
|
|
57
|
-
msgs: string[];
|
|
58
|
-
} | null>;
|
|
59
|
-
whisper(recipientId: number, message: string): Promise<void>;
|
|
60
|
-
kmail(recipientId: number, message: string): Promise<void>;
|
|
61
|
-
getMallPrice(itemId: number): Promise<MallPrice>;
|
|
62
|
-
getItemDescription(descId: number): Promise<{
|
|
63
|
-
melting: boolean;
|
|
64
|
-
singleEquip: boolean;
|
|
65
|
-
blueText: string;
|
|
66
|
-
effect?: {
|
|
67
|
-
name: string;
|
|
68
|
-
duration: number;
|
|
69
|
-
descid: string;
|
|
70
|
-
};
|
|
71
|
-
}>;
|
|
72
|
-
getEffectDescription(descId: string): Promise<{
|
|
73
|
-
blueText: string;
|
|
74
|
-
}>;
|
|
75
|
-
getSkillDescription(id: number): Promise<{
|
|
76
|
-
blueText: string;
|
|
77
|
-
}>;
|
|
78
|
-
joinClan(id: number): Promise<boolean>;
|
|
79
|
-
addToWhitelist(playerId: number, clanId: number): Promise<boolean>;
|
|
80
|
-
getLeaderboard(leaderboardId: number): Promise<import("./utils/leaderboard.js").LeaderboardInfo>;
|
|
81
|
-
useFamiliar(familiarId: number): Promise<boolean>;
|
|
82
|
-
getFamiliars(): Promise<Familiar[]>;
|
|
83
|
-
}
|
|
84
|
-
export {};
|
package/dist/Client.js
DELETED
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
import { Mutex } from "async-mutex";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
import { sanitiseBlueText, wait } from "./utils/utils.js";
|
|
4
|
-
import { Player } from "./Player.js";
|
|
5
|
-
import { createBody, createSession, formatQuerystring } from "./utils/visit.js";
|
|
6
|
-
import { parseLeaderboard } from "./utils/leaderboard.js";
|
|
7
|
-
import { isValidMessage, } from "./utils/kmail.js";
|
|
8
|
-
import { PlayerCache } from "./Cache.js";
|
|
9
|
-
export class Client extends EventEmitter {
|
|
10
|
-
static loginMutex = new Mutex();
|
|
11
|
-
actionMutex = new Mutex();
|
|
12
|
-
session = createSession();
|
|
13
|
-
players = new PlayerCache(this);
|
|
14
|
-
#username;
|
|
15
|
-
#password;
|
|
16
|
-
#isRollover = false;
|
|
17
|
-
#chatBotStarted = false;
|
|
18
|
-
#pwd = "";
|
|
19
|
-
lastFetchedMessages = "0";
|
|
20
|
-
postRolloverLatch = false;
|
|
21
|
-
constructor(username, password) {
|
|
22
|
-
super();
|
|
23
|
-
this.#username = username;
|
|
24
|
-
this.#password = password;
|
|
25
|
-
}
|
|
26
|
-
async #fetch(path, options = {}) {
|
|
27
|
-
const { params, body, method } = {
|
|
28
|
-
params: {},
|
|
29
|
-
body: undefined,
|
|
30
|
-
method: "POST",
|
|
31
|
-
...options,
|
|
32
|
-
};
|
|
33
|
-
const qs = formatQuerystring({ ...params, pwd: this.#pwd });
|
|
34
|
-
return await this.session(`https://www.kingdomofloathing.com/${path}${qs ? `?${qs}` : ""}`, {
|
|
35
|
-
method,
|
|
36
|
-
body: body ? createBody({ ...body, pwd: this.#pwd }) : undefined,
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
async fetchText(path, options = {}) {
|
|
40
|
-
if (!(await this.login()))
|
|
41
|
-
return options.fallback ?? "";
|
|
42
|
-
return (await this.#fetch(path, options)).text();
|
|
43
|
-
}
|
|
44
|
-
async fetchJson(path, options = {}) {
|
|
45
|
-
if (!(await this.login()))
|
|
46
|
-
return options.fallback ?? null;
|
|
47
|
-
return (await this.#fetch(path, options)).json();
|
|
48
|
-
}
|
|
49
|
-
async login() {
|
|
50
|
-
if (this.#isRollover)
|
|
51
|
-
return false;
|
|
52
|
-
return Client.loginMutex.runExclusive(async () => {
|
|
53
|
-
if (await this.checkLoggedIn())
|
|
54
|
-
return true;
|
|
55
|
-
if (this.#isRollover)
|
|
56
|
-
return false;
|
|
57
|
-
try {
|
|
58
|
-
const response = await this.#fetch("login.php", {
|
|
59
|
-
body: {
|
|
60
|
-
loggingin: "Yup.",
|
|
61
|
-
loginname: this.#username,
|
|
62
|
-
password: this.#password,
|
|
63
|
-
secure: "0",
|
|
64
|
-
submitbutton: "Log In",
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
await response.text();
|
|
68
|
-
if (!(await this.checkLoggedIn()))
|
|
69
|
-
return false;
|
|
70
|
-
if (this.postRolloverLatch) {
|
|
71
|
-
this.postRolloverLatch = false;
|
|
72
|
-
this.emit("rollover");
|
|
73
|
-
}
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
console.log("error", error);
|
|
78
|
-
// Login failed, let's check if it is due to rollover
|
|
79
|
-
await this.#checkForRollover();
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
isRollover() {
|
|
85
|
-
return this.#isRollover;
|
|
86
|
-
}
|
|
87
|
-
async checkLoggedIn() {
|
|
88
|
-
try {
|
|
89
|
-
const response = await this.#fetch("api.php", {
|
|
90
|
-
params: { what: "status", for: `${this.#username} bot` },
|
|
91
|
-
});
|
|
92
|
-
const api = (await response.json());
|
|
93
|
-
this.#pwd = api.pwd;
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async #checkForRollover() {
|
|
101
|
-
const isRollover = /The system is currently down for nightly maintenance/.test(await this.fetchText("/"));
|
|
102
|
-
if (this.#isRollover && !isRollover) {
|
|
103
|
-
// Set the post-rollover latch so the bot can react on next log in.
|
|
104
|
-
this.postRolloverLatch = true;
|
|
105
|
-
}
|
|
106
|
-
this.#isRollover = isRollover;
|
|
107
|
-
if (this.#isRollover) {
|
|
108
|
-
// Rollover appears to be in progress. Check again in one minute.
|
|
109
|
-
setTimeout(() => this.#checkForRollover(), 60000);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async startChatBot() {
|
|
113
|
-
if (this.#chatBotStarted)
|
|
114
|
-
return;
|
|
115
|
-
await this.useChatMacro("/join talkie");
|
|
116
|
-
this.loopChatBot();
|
|
117
|
-
this.#chatBotStarted = true;
|
|
118
|
-
}
|
|
119
|
-
async loopChatBot() {
|
|
120
|
-
await Promise.all([this.checkMessages(), this.checkKmails()]);
|
|
121
|
-
await wait(3000);
|
|
122
|
-
await this.loopChatBot();
|
|
123
|
-
}
|
|
124
|
-
async checkMessages() {
|
|
125
|
-
const newChatMessagesResponse = await this.fetchJson("newchatmessages.php", {
|
|
126
|
-
params: {
|
|
127
|
-
j: 1,
|
|
128
|
-
lasttime: this.lastFetchedMessages,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
if (!newChatMessagesResponse || typeof newChatMessagesResponse !== "object")
|
|
132
|
-
return;
|
|
133
|
-
this.lastFetchedMessages = newChatMessagesResponse["last"];
|
|
134
|
-
newChatMessagesResponse["msgs"]
|
|
135
|
-
.filter(isValidMessage)
|
|
136
|
-
.map((msg) => ({
|
|
137
|
-
type: msg.type,
|
|
138
|
-
who: new Player(this, Number(msg.who.id), msg.who.name),
|
|
139
|
-
msg: msg.msg,
|
|
140
|
-
time: new Date(Number(msg.time) * 1000),
|
|
141
|
-
}))
|
|
142
|
-
.forEach((message) => {
|
|
143
|
-
switch (message.type) {
|
|
144
|
-
case "public":
|
|
145
|
-
return void this.emit("public", message);
|
|
146
|
-
case "private":
|
|
147
|
-
return void this.emit("whisper", message);
|
|
148
|
-
case "system":
|
|
149
|
-
return void this.emit("system", message);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
async checkKmails() {
|
|
154
|
-
const newKmailsResponse = await this.fetchJson("api.php", {
|
|
155
|
-
params: {
|
|
156
|
-
what: "kmail",
|
|
157
|
-
for: `${this.#username} bot`,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
if (!Array.isArray(newKmailsResponse) || newKmailsResponse.length === 0)
|
|
161
|
-
return;
|
|
162
|
-
const newKmails = newKmailsResponse.map((msg) => ({
|
|
163
|
-
type: "kmail",
|
|
164
|
-
who: new Player(this, Number(msg.fromid), msg.fromname),
|
|
165
|
-
msg: msg.message,
|
|
166
|
-
time: new Date(Number(msg.azunixtime) * 1000),
|
|
167
|
-
}));
|
|
168
|
-
const data = {
|
|
169
|
-
the_action: "delete",
|
|
170
|
-
box: "Inbox",
|
|
171
|
-
...Object.fromEntries(newKmailsResponse.map(({ id }) => [`sel${id}`, "on"])),
|
|
172
|
-
};
|
|
173
|
-
await this.fetchText("messages.php", { params: data });
|
|
174
|
-
newKmails.forEach((m) => this.emit("kmail", m));
|
|
175
|
-
}
|
|
176
|
-
async sendChat(message) {
|
|
177
|
-
return await this.fetchJson("submitnewchat.php", {
|
|
178
|
-
params: {
|
|
179
|
-
graf: message,
|
|
180
|
-
j: 1,
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
async useChatMacro(macro) {
|
|
185
|
-
return await this.sendChat(`/clan ${macro}`);
|
|
186
|
-
}
|
|
187
|
-
async whisper(recipientId, message) {
|
|
188
|
-
await this.useChatMacro(`/w ${recipientId} ${message}`);
|
|
189
|
-
}
|
|
190
|
-
async kmail(recipientId, message) {
|
|
191
|
-
await this.fetchText("sendmessage.php", {
|
|
192
|
-
params: {
|
|
193
|
-
action: "send",
|
|
194
|
-
j: 1,
|
|
195
|
-
towho: recipientId,
|
|
196
|
-
contact: 0,
|
|
197
|
-
message: message,
|
|
198
|
-
howmany1: 1,
|
|
199
|
-
whichitem1: 0,
|
|
200
|
-
sendmeat: 0,
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
async getMallPrice(itemId) {
|
|
205
|
-
const prices = await this.fetchText("backoffice.php", {
|
|
206
|
-
params: {
|
|
207
|
-
action: "prices",
|
|
208
|
-
ajax: 1,
|
|
209
|
-
iid: itemId,
|
|
210
|
-
},
|
|
211
|
-
});
|
|
212
|
-
const unlimitedMatch = prices.match(/<td>unlimited:<\/td><td><b>(?<unlimitedPrice>[\d,]+)/);
|
|
213
|
-
const limitedMatch = prices.match(/<td>limited:<\/td><td><b>(?<limitedPrice>[\d,]+)/);
|
|
214
|
-
const unlimitedPrice = unlimitedMatch
|
|
215
|
-
? parseInt(unlimitedMatch[1].replace(/,/g, ""))
|
|
216
|
-
: 0;
|
|
217
|
-
const limitedPrice = limitedMatch
|
|
218
|
-
? parseInt(limitedMatch[1].replace(/,/g, ""))
|
|
219
|
-
: 0;
|
|
220
|
-
let minPrice = limitedMatch ? limitedPrice : null;
|
|
221
|
-
minPrice = unlimitedMatch
|
|
222
|
-
? !minPrice || unlimitedPrice < minPrice
|
|
223
|
-
? unlimitedPrice
|
|
224
|
-
: minPrice
|
|
225
|
-
: minPrice;
|
|
226
|
-
const formattedMinPrice = minPrice
|
|
227
|
-
? (minPrice === unlimitedPrice
|
|
228
|
-
? unlimitedMatch?.[1]
|
|
229
|
-
: limitedMatch?.[1]) ?? ""
|
|
230
|
-
: "";
|
|
231
|
-
return {
|
|
232
|
-
mallPrice: unlimitedPrice,
|
|
233
|
-
limitedMallPrice: limitedPrice,
|
|
234
|
-
formattedMinPrice: formattedMinPrice,
|
|
235
|
-
minPrice: minPrice,
|
|
236
|
-
formattedMallPrice: unlimitedMatch ? unlimitedMatch[1] : "",
|
|
237
|
-
formattedLimitedMallPrice: limitedMatch ? limitedMatch[1] : "",
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
async getItemDescription(descId) {
|
|
241
|
-
const description = await this.fetchText("desc_item.php", {
|
|
242
|
-
params: {
|
|
243
|
-
whichitem: descId,
|
|
244
|
-
},
|
|
245
|
-
});
|
|
246
|
-
const blueText = description.match(/<center>\s*<b>\s*<font color="?[\w]+"?>(?<description>[\s\S]+)<\/center>/i);
|
|
247
|
-
const effect = description.match(/Effect: \s?<b>\s?<a[^>]+href="desc_effect\.php\?whicheffect=(?<descid>[^"]+)[^>]+>(?<effect>[\s\S]+)<\/a>[^(]+\((?<duration>[\d]+)/);
|
|
248
|
-
const melting = description.match(/This item will disappear at the end of the day\./);
|
|
249
|
-
const singleEquip = description.match(/ You may not equip more than one of these at a time\./);
|
|
250
|
-
return {
|
|
251
|
-
melting: !!melting,
|
|
252
|
-
singleEquip: !!singleEquip,
|
|
253
|
-
blueText: sanitiseBlueText(blueText?.groups?.description),
|
|
254
|
-
effect: effect?.groups
|
|
255
|
-
? {
|
|
256
|
-
name: effect.groups?.name,
|
|
257
|
-
duration: Number(effect.groups?.duration) || 0,
|
|
258
|
-
descid: effect.groups?.descid,
|
|
259
|
-
}
|
|
260
|
-
: undefined,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
async getEffectDescription(descId) {
|
|
264
|
-
const description = await this.fetchText("desc_effect.php", {
|
|
265
|
-
params: {
|
|
266
|
-
whicheffect: descId,
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
const blueText = description.match(/<center><font color="?[\w]+"?>(?<description>[\s\S]+)<\/div>/m);
|
|
270
|
-
return { blueText: sanitiseBlueText(blueText?.groups?.description) };
|
|
271
|
-
}
|
|
272
|
-
async getSkillDescription(id) {
|
|
273
|
-
const description = await this.fetchText("desc_skill.php", {
|
|
274
|
-
params: {
|
|
275
|
-
whichskill: String(id),
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
const blueText = description.match(/<blockquote[\s\S]+<[Cc]enter>(?<description>[\s\S]+)<\/[Cc]enter>/);
|
|
279
|
-
return { blueText: sanitiseBlueText(blueText?.groups?.description) };
|
|
280
|
-
}
|
|
281
|
-
async joinClan(id) {
|
|
282
|
-
const result = await this.fetchText("showclan.php", {
|
|
283
|
-
params: {
|
|
284
|
-
whichclan: id,
|
|
285
|
-
action: "joinclan",
|
|
286
|
-
confirm: "on",
|
|
287
|
-
},
|
|
288
|
-
});
|
|
289
|
-
return (result.includes("clanhalltop.gif") ||
|
|
290
|
-
result.includes("a clan you're already in"));
|
|
291
|
-
}
|
|
292
|
-
async addToWhitelist(playerId, clanId) {
|
|
293
|
-
return await this.actionMutex.runExclusive(async () => {
|
|
294
|
-
if (!(await this.joinClan(clanId)))
|
|
295
|
-
return false;
|
|
296
|
-
await this.fetchText("clan_whitelist.php", {
|
|
297
|
-
params: {
|
|
298
|
-
addwho: playerId,
|
|
299
|
-
level: 2,
|
|
300
|
-
title: "",
|
|
301
|
-
action: "add",
|
|
302
|
-
},
|
|
303
|
-
});
|
|
304
|
-
return true;
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
async getLeaderboard(leaderboardId) {
|
|
308
|
-
const page = await this.fetchText("museum.php", {
|
|
309
|
-
params: {
|
|
310
|
-
floor: 1,
|
|
311
|
-
place: "leaderboards",
|
|
312
|
-
whichboard: leaderboardId,
|
|
313
|
-
},
|
|
314
|
-
});
|
|
315
|
-
return parseLeaderboard(page);
|
|
316
|
-
}
|
|
317
|
-
async useFamiliar(familiarId) {
|
|
318
|
-
const result = await this.fetchText("familiar.php", {
|
|
319
|
-
params: {
|
|
320
|
-
action: "newfam",
|
|
321
|
-
newfam: familiarId.toFixed(0),
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
return result.includes(`var currentfam = ${familiarId};`);
|
|
325
|
-
}
|
|
326
|
-
async getFamiliars() {
|
|
327
|
-
const terrarium = await this.fetchText("familiar.php");
|
|
328
|
-
const matches = terrarium.matchAll(/onClick='fam\((\d+)\)'(?:><img)? src=".*?\/(\w+\/\w+.(?:gif|png))".*?\d+-pound (.*?) \(/g);
|
|
329
|
-
const familiars = [...matches].map((m) => ({
|
|
330
|
-
id: Number(m[1]),
|
|
331
|
-
image: m[2],
|
|
332
|
-
name: m[3],
|
|
333
|
-
}));
|
|
334
|
-
if (terrarium.includes("fam(278)")) {
|
|
335
|
-
familiars.push({
|
|
336
|
-
id: 278,
|
|
337
|
-
image: "otherimages/righthandbody.png",
|
|
338
|
-
name: "Left-Hand Man",
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
return familiars;
|
|
342
|
-
}
|
|
343
|
-
}
|
package/dist/Client.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|