Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/loathers/keeping-tabs into …
Browse files Browse the repository at this point in the history
…coinmaster
  • Loading branch information
Tokoeka committed Jan 29, 2024
2 parents fb71d6a + 31ee178 commit c2f8e10
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 7 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ Naming a tab involves specifying what to do with all items in that tab by naming
### Actions

* `mall`
* This will add the item to your mall store
* This will add the item to your mall store at the maximum possible price
* `low`
* This will add the item to your mall store at the 5th lowest price currently listed
* `autosell`
* This will autosell the item
* `sell`
* This will either autosell the item or add it to your mall store. It will add it to your mall store only if there are less than 1000 stocked at autosell price
* This will either autosell the item or add it to your mall store at the maximum possible price. It will add it to your mall store only if there are less than 1000 stocked at autosell price
* `use`
* This will use the item
* `display`
Expand All @@ -59,6 +61,8 @@ All options are supported in all tabs, unless specified. They are white space se

* `keepN`
* Keeps `N` copies of the item after running
* `stockN`
* (only supported by `mall`, `display`, and `closet`). Ensures `N` copies of the item are stocked in the relevant locations, keeps the rest in your inventory
* `<N`
* Only performs the given operation on items that have a `mallPrice` that is less than `N`
* `>N`
Expand Down Expand Up @@ -155,7 +159,8 @@ Use `keeping-tabs debug help` to see a full list of available debug commands.

## TODO

* [ ] Add more mall options (add at fixed price, add at min price, limit the items for sale)
* [ ] Add more mall options (add at fixed price, limit the items for sale)
* [x] Add more mall options (add at min price)
* [ ] Add confirmation for kmailing, optionally?
* [x] Add option to keep certain number of items (using format of keepN)
* [ ] Add `pull` to pull specific items from Hagnks
Expand Down
47 changes: 46 additions & 1 deletion src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
autosell,
autosellPrice,
cliExecute,
closetAmount,
displayAmount,
isCoinmasterItem,
Item,
itemAmount,
Expand All @@ -10,6 +12,7 @@ import {
putCloset,
putDisplay,
putShop,
shopAmount,
use,
wellStocked,
} from "kolmafia";
Expand Down Expand Up @@ -44,20 +47,53 @@ export const actions: {
};
} = {
mall: (options: Options) => {
if (options.stock) {
return {
action: (item: Item) =>
putShop(
0,
0,
Math.min(Math.max(0, (options.stock ?? 0) - shopAmount(item)), amount(item, options)),
item
),
};
}
return { action: (item: Item) => putShop(0, 0, amount(item, options), item) };
},
sell: (options: Options) => {
return {
action: (item: Item) => {
if (wellStocked(`${item}`, 1000, Math.max(100, autosellPrice(item) * 2))) {
if (
wellStocked(`${item}`, 1000, Math.max(100, autosellPrice(item) * 2)) ||
!item.tradeable
) {
autosell(amount(item, options), item);
} else {
putShop(0, 0, amount(item, options), item);
}
},
};
},
low: (options) => {
return {
action: (item) => {
putShop(mallPrice(item), 0, amount(item, options), item);
},
};
},
display: (options: Options) => {
if (options.stock) {
return {
action: (item: Item) =>
putDisplay(
Math.min(
Math.max(0, (options.stock ?? 0) - displayAmount(item)),
amount(item, options)
),
item
),
};
}
return { action: (item: Item) => putDisplay(amount(item, options), item) };
},
use: (options: Options) => {
Expand All @@ -82,6 +118,15 @@ export const actions: {
};
},
closet: (options: Options) => {
if (options.stock) {
return {
action: (item: Item) =>
putDisplay(
Math.min(Math.max(0, (options.stock ?? 0) - closetAmount(item)), amount(item, options)),
item
),
};
}
return {
action: (item: Item) => putCloset(amount(item, options), item),
};
Expand Down
89 changes: 89 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,95 @@ import { set } from "libram";
const HIGHLIGHT = isDarkMode() ? "yellow" : "blue";
const DEFAULT_ACTIONS = "closet use coinmaster mall autosell display sell kmail fuel collection";

function items(tabId: TabId, type: InventoryType): Item[] {
const tab = visitUrl(`${type}.php?which=f${tabId}`);
const regexp = /ic(\d+)/g;
const items: Item[] = [];

let match;
while ((match = regexp.exec(tab)) !== null) {
const item = toItem(toInt(match[1]));
items.push(item);
}
return items;
}

function notesText(): string {
const questLogNotesHtml = visitUrl("questlog.php?which=4");
return questLogNotesHtml.substring(
questLogNotesHtml.indexOf(">", questLogNotesHtml.indexOf("<textarea")) + 1,
questLogNotesHtml.indexOf("</textarea")
);
}

function tabAliases(): Map<string, string> {
const questLogAliases: RegExpExecArray[] = notesText()
.split("\n")
.map((s) => /keeping-tabs: ?([A-Za-z0-9\- ]+)=(.*)/g.exec(s))
.filter((r) => r !== null) as RegExpExecArray[];

const values: [string, string][] = questLogAliases.map((r) => [r[1], r[2]]);
return new Map(values);
}

function tabCollections(): Map<string, Item[]> {
const questLogEntries: RegExpExecArray[] = notesText()
.split("\n")
.map((s) => /keeping-tabs-collection: ?'(.*)'=([0-9,]+)/g.exec(s))
.filter((r) => r !== null) as RegExpExecArray[];

const values: [string, Item[]][] = questLogEntries.map((r) => [
r[1],
r[2].split(",").map((i) => toItem(toInt(i))),
]);
return new Map(values);
}

function favoriteTabs(): Tab[] {
// visit the consumables tab to ensure that you get clickable links for
// all favorite tabs
const inventory = visitUrl(`inventory.php?which=1`);
const tabRegex =
/<a href="inventory.php\?which=f(\d+)">([A-Za-z0-9;&]+)(:[A-Za-z0-9;&\-#,<>=]+)?<\/a>/g;
const aliasRegex = /([A-Za-z0-9;&]+)(:[A-Za-z0-9;&\-#,<>=]+)?/g;

const tabs: Tab[] = [];
const aliases = tabAliases();

let match;
let aliasMatch;

while ((match = tabRegex.exec(inventory)) !== null) {
const title = match[2];
const options = match[3];
const alias = aliases.get(title);
const id = parseInt(match[1]);

if (isTabTitle(title)) {
tabs.push({
title,
id,
options: (options ?? ":").substring(1).split(","),
type: "inventory",
});
} else if (alias && (aliasMatch = aliasRegex.exec(alias))) {
const aliasTitle = aliasMatch[1];
const options = aliasMatch[2];
if (isTabTitle(aliasTitle)) {
tabs.push({
title: aliasTitle,
id: parseInt(match[1]),
options: (options ?? ":").substring(1).split(","),
type: "inventory",
alias: title,
});
}
}
}

return tabs;
}

function tabString(tab: Tab): string {
const options = Options.parse(tab.options);
const title = tab.alias ? `${tab.title} (alias ${tab.alias})` : tab.title;
Expand Down
9 changes: 9 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type OptionsParams = {

export class Options {
keep?: number;
stock?: number;
target?: string;
body?: string;
priceUpperThreshold?: number;
Expand All @@ -28,6 +29,11 @@ export class Options {
options.keep = parseInt(keep[1]);
continue;
}
const stock = optionStr.match(/stock(\d+)/);
if (stock && stock[1]) {
options.stock = parseInt(stock[1]);
continue;
}
const target = optionStr.match(/#(.*)/);
if (target && target[1]) {
options.target = target[1];
Expand Down Expand Up @@ -65,6 +71,9 @@ export class Options {
if (this.keep) {
optionsStr.push(`keep: ${this.keep}`);
}
if (this.stock) {
optionsStr.push(`stock: ${this.stock}`);
}
if (this.target) {
optionsStr.push(`target: ${this.target}`);
}
Expand Down
7 changes: 4 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ export const ALL_TAB_TITLES = [
"closet",
"fuel",
"collection",
"low",
"coinmaster",
] as const;
export type TabTitle = (typeof ALL_TAB_TITLES)[number];
export type TabTitle = typeof ALL_TAB_TITLES[number];
export type TabId = number;

export function isTabTitle(value: string): value is TabTitle {
return ALL_TAB_TITLES.includes(value as TabTitle);
}

const ALL_ACTION_OPTIONS = ["keep", "target"] as const;
export type ActionOption = (typeof ALL_ACTION_OPTIONS)[number];
const ALL_ACTION_OPTIONS = ["keep", "stock", "target"] as const;
export type ActionOption = typeof ALL_ACTION_OPTIONS[number];

export function isActionOption(value: string): value is ActionOption {
return ALL_ACTION_OPTIONS.includes(value as ActionOption);
Expand Down

0 comments on commit c2f8e10

Please sign in to comment.