rename economy plugins and migrate legacy ids

This commit is contained in:
Franz Rolfsvaag 2026-06-17 22:32:51 +02:00
parent 471218a79d
commit 580b4392b4
23 changed files with 1430 additions and 1060 deletions

View File

@ -124,6 +124,7 @@ This file tracks larger Lumi work that cannot safely be completed in one pass. K
- Review localization/translation keys if present so simplified wording remains consistent across languages.
## Done
- 2026-06-17: Renamed all remaining Economy internals from the old misspelled IDs/paths/tables to `economy-*`, added startup migration for legacy plugin rows, settings, command usage IDs, tables, uploads, asset paths, old URLs, and bumped core/plugin patch versions.
- 2026-06-17: Fixed user-facing Economy spelling, restored `/stats/{username}` Compare toggling, linked Top commands run leaderboard entries to `/commands`, and bumped core/plugin patch versions.
- 2026-06-17: Fixed custom command Edit buttons and `/commands` Copy Link / expand buttons with delegated handlers, clipboard fallback, and v0.1.5 patch bump.
- 2026-06-17: Fixed repo-based core updates deleting `data/update-cache/repo` during apply, added a verification guard, and bumped core package version to v0.1.4.

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "lumi-bot",
"version": "0.1.6",
"version": "0.1.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lumi-bot",
"version": "0.1.6",
"version": "0.1.7",
"dependencies": {
"adm-zip": "^0.5.12",
"better-sqlite3": "^11.5.0",

View File

@ -1,6 +1,6 @@
{
"name": "lumi-bot",
"version": "0.1.6",
"version": "0.1.7",
"private": true,
"type": "commonjs",
"scripts": {

View File

@ -591,7 +591,7 @@ function reserveDelivery(db, userId, deliveryKey, type, status, details) {
function grantGiftOnce(db, userId, deliveryKey, mode) {
const config = getConfig(db);
const amount = config.gift_amount;
const framework = global.lumiFrameworks?.echonomy;
const framework = global.lumiFrameworks?.economy;
if (!amount || !framework || typeof framework.addBalance !== "function") {
return null;
}
@ -919,7 +919,7 @@ async function buildDiagnostics(discordClient, config) {
discordAvailable: Boolean(discordClient),
discordReady: Boolean(discordClient?.readyAt),
channel,
echonomyAvailable: Boolean(global.lumiFrameworks?.echonomy?.addBalance),
economyAvailable: Boolean(global.lumiFrameworks?.economy?.addBalance),
currentDate: getZonedDateParts(config.timezone)
};
}

View File

@ -1,7 +1,7 @@
{
"id": "birthday",
"name": "Birthday",
"version": "0.1.1",
"version": "0.1.2",
"description": "Birthday profiles, announcements, lookup commands, and optional birthday currency gifts.",
"main": "index.js"
}

View File

@ -12,7 +12,7 @@
<tbody>
<tr><th>Discord client</th><td><%= diagnostics.discordAvailable ? (diagnostics.discordReady ? "Ready" : "Available") : "Unavailable" %></td></tr>
<tr><th>Announcement channel</th><td><%= diagnostics.channel.message %></td></tr>
<tr><th>Economy</th><td><%= diagnostics.echonomyAvailable ? "Available" : "Unavailable" %></td></tr>
<tr><th>Economy</th><td><%= diagnostics.economyAvailable ? "Available" : "Unavailable" %></td></tr>
<tr><th>Current plugin date</th><td><%= diagnostics.currentDate.year %>-<%= String(diagnostics.currentDate.month).padStart(2, "0") %>-<%= String(diagnostics.currentDate.day).padStart(2, "0") %></td></tr>
</tbody>
</table>

View File

@ -1,5 +1,5 @@
{
"pluginId": "echonomy-framework",
"pluginId": "economy-framework",
"pluginName": "Economy Framework",
"platformKeys": {
"discord": "platform_discord",

View File

@ -7,7 +7,11 @@ const multer = require("multer");
const EventEmitter = require("events");
const { ensureUserForIdentity } = require("../../src/services/users");
const PLUGIN_ID = "echonomy-framework";
const PLUGIN_ID = "economy-framework";
const LEGACY_STEM = ["echo", "nomy"].join("");
const LEGACY_PLUGIN_ID = `${LEGACY_STEM}-framework`;
const LEGACY_FRAMEWORK_KEY = LEGACY_STEM;
const LEGACY_NAME_PROVIDER = `${LEGACY_STEM}_name`;
const DEFAULT_SETTINGS = {
currency_name: "Coin",
currency_name_plural: "Coins",
@ -195,6 +199,8 @@ module.exports = {
twitchClient
}) {
settingsApi = settings;
const repoRoot = path.join(__dirname, "..", "..");
migrateLegacyInstall(db, repoRoot);
ensureTables(db);
ensureDefaults(db);
startActivityRewardFlusher(db);
@ -207,8 +213,7 @@ module.exports = {
attachTwitchListeners({ db, settings, twitchClient });
installProfileHook(app, () => getConfig(db));
const repoRoot = path.join(__dirname, "..", "..");
const uploadDir = path.join(repoRoot, "data", "echonomy-framework");
const uploadDir = path.join(repoRoot, "data", "economy-framework");
fs.mkdirSync(uploadDir, { recursive: true });
const upload = multer({
dest: uploadDir,
@ -239,7 +244,7 @@ module.exports = {
const events = getCustomEvents(config);
const responses = Object.values(config.responses || {});
res.render(path.join(__dirname, "views", "echonomy.ejs"), {
res.render(path.join(__dirname, "views", "economy.ejs"), {
title: "Economy Framework",
config,
user,
@ -707,7 +712,7 @@ module.exports = {
return res.redirect("/profile/banking");
}
const fund = db
.prepare("SELECT * FROM echonomy_pots WHERE id = ?")
.prepare("SELECT * FROM economy_pots WHERE id = ?")
.get(req.params.id);
if (!fund || fund.status !== "active") {
req.session.flash = {
@ -737,6 +742,7 @@ module.exports = {
role: "public",
section: "plugins"
});
mountLegacyRedirect(web, LEGACY_PLUGIN_ID, PLUGIN_ID);
web.mount("/profile/banking", bankRouter);
}
};
@ -750,13 +756,13 @@ function deny(res) {
function ensureTables(db) {
db.exec(`
CREATE TABLE IF NOT EXISTS echonomy_accounts (
CREATE TABLE IF NOT EXISTS economy_accounts (
user_id TEXT PRIMARY KEY,
balance INTEGER NOT NULL DEFAULT 0,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS echonomy_transactions (
CREATE TABLE IF NOT EXISTS economy_transactions (
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
amount INTEGER NOT NULL,
@ -767,7 +773,7 @@ function ensureTables(db) {
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS echonomy_pots (
CREATE TABLE IF NOT EXISTS economy_pots (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT,
@ -778,7 +784,7 @@ function ensureTables(db) {
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS echonomy_pot_contributions (
CREATE TABLE IF NOT EXISTS economy_pot_contributions (
id TEXT PRIMARY KEY,
pot_id TEXT NOT NULL,
user_id TEXT NOT NULL,
@ -786,7 +792,7 @@ function ensureTables(db) {
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS echonomy_activity_reward_hourly (
CREATE TABLE IF NOT EXISTS economy_activity_reward_hourly (
user_id TEXT NOT NULL,
hour_start INTEGER NOT NULL,
source TEXT NOT NULL,
@ -796,14 +802,162 @@ function ensureTables(db) {
PRIMARY KEY (user_id, hour_start, source)
);
CREATE INDEX IF NOT EXISTS echonomy_transactions_created_at_idx
ON echonomy_transactions (created_at);
CREATE INDEX IF NOT EXISTS economy_transactions_created_at_idx
ON economy_transactions (created_at);
CREATE INDEX IF NOT EXISTS echonomy_activity_reward_hourly_hour_idx
ON echonomy_activity_reward_hourly (hour_start);
CREATE INDEX IF NOT EXISTS economy_activity_reward_hourly_hour_idx
ON economy_activity_reward_hourly (hour_start);
`);
}
function migrateLegacyInstall(db, repoRoot) {
migratePluginSettings(db);
migrateLegacyTables(db);
migrateLegacyNameProvider(db);
migrateLegacyIconPath(db);
migrateLegacyUploadDir(repoRoot);
mergeCommandUsage(db, `${LEGACY_FRAMEWORK_KEY}:root`, "economy:root");
}
function migratePluginSettings(db) {
const rows = db
.prepare("SELECT key, value, updated_at FROM plugin_settings WHERE plugin_id = ?")
.all(LEGACY_PLUGIN_ID);
if (!rows.length) {
return;
}
const insert = db.prepare(
"INSERT OR IGNORE INTO plugin_settings (plugin_id, key, value, updated_at) VALUES (?, ?, ?, ?)"
);
for (const row of rows) {
insert.run(PLUGIN_ID, row.key, row.value, row.updated_at || Date.now());
}
db.prepare("DELETE FROM plugin_settings WHERE plugin_id = ?").run(LEGACY_PLUGIN_ID);
}
function migrateLegacyTables(db) {
[
"accounts",
"transactions",
"pots",
"pot_contributions",
"activity_reward_hourly"
].forEach((suffix) => {
migrateTable(db, `${LEGACY_STEM}_${suffix}`, `economy_${suffix}`);
});
}
function migrateTable(db, legacyName, currentName) {
if (!tableExists(db, legacyName)) {
return;
}
if (!tableExists(db, currentName)) {
db.prepare(`ALTER TABLE ${quoteIdentifier(legacyName)} RENAME TO ${quoteIdentifier(currentName)}`).run();
return;
}
db.prepare(
`INSERT OR IGNORE INTO ${quoteIdentifier(currentName)} SELECT * FROM ${quoteIdentifier(legacyName)}`
).run();
db.prepare(`DROP TABLE ${quoteIdentifier(legacyName)}`).run();
}
function migrateLegacyNameProvider(db) {
if (!tableExists(db, "linked_accounts")) {
return;
}
db.prepare("UPDATE linked_accounts SET provider = ? WHERE provider = ?").run(
"economy_name",
LEGACY_NAME_PROVIDER
);
}
function migrateLegacyIconPath(db) {
const legacyPrefix = `/plugins/${LEGACY_PLUGIN_ID}/assets/`;
const currentPrefix = `/plugins/${PLUGIN_ID}/assets/`;
const rows = db
.prepare(
"SELECT key, value FROM plugin_settings WHERE plugin_id = ? AND key = 'currency_icon_path'"
)
.all(PLUGIN_ID);
for (const row of rows) {
if (row.value && row.value.startsWith(legacyPrefix)) {
setPluginSetting(db, row.key, row.value.replace(legacyPrefix, currentPrefix));
}
}
}
function migrateLegacyUploadDir(repoRoot) {
const legacyDir = path.join(repoRoot, "data", LEGACY_PLUGIN_ID);
const currentDir = path.join(repoRoot, "data", PLUGIN_ID);
if (!fs.existsSync(legacyDir)) {
return;
}
if (!fs.existsSync(currentDir)) {
fs.renameSync(legacyDir, currentDir);
return;
}
mergeDirectory(legacyDir, currentDir);
}
function mergeDirectory(sourceDir, targetDir) {
fs.mkdirSync(targetDir, { recursive: true });
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
const sourcePath = path.join(sourceDir, entry.name);
const targetPath = path.join(targetDir, entry.name);
if (!fs.existsSync(targetPath)) {
fs.renameSync(sourcePath, targetPath);
continue;
}
if (entry.isDirectory()) {
mergeDirectory(sourcePath, targetPath);
}
}
try {
fs.rmSync(sourceDir, { recursive: true, force: true });
} catch {
// Leaving the old directory is safer than failing startup.
}
}
function mergeCommandUsage(db, legacyId, currentId) {
if (!tableExists(db, "command_usage")) {
return;
}
const legacy = db
.prepare("SELECT count, updated_at FROM command_usage WHERE command_id = ?")
.get(legacyId);
if (!legacy) {
return;
}
db.prepare(
"INSERT INTO command_usage (command_id, count, updated_at) VALUES (?, ?, ?) " +
"ON CONFLICT(command_id) DO UPDATE SET count = command_usage.count + excluded.count, updated_at = MAX(command_usage.updated_at, excluded.updated_at)"
).run(currentId, legacy.count || 0, legacy.updated_at || Date.now());
db.prepare("DELETE FROM command_usage WHERE command_id = ?").run(legacyId);
}
function tableExists(db, name) {
return Boolean(
db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(name)
);
}
function quoteIdentifier(name) {
return `"${name.replace(/"/g, '""')}"`;
}
function mountLegacyRedirect(web, legacyId, currentId) {
const legacyMount = `/plugins/${legacyId}`;
const router = web.createRouter();
router.use((req, res) => {
const suffix = req.originalUrl.startsWith(legacyMount)
? req.originalUrl.slice(legacyMount.length)
: "";
res.redirect(308, `/plugins/${currentId}${suffix || ""}`);
});
web.mount(legacyMount, router);
}
function ensureDefaults(db) {
const existing = getPluginSettings(db);
for (const [key, value] of Object.entries(DEFAULT_SETTINGS)) {
@ -1058,7 +1212,7 @@ function queueActivityReward(
const numericHits = Number.isFinite(Number(hits)) ? Number(hits) : 0;
const numericMinutes = Number.isFinite(Number(minutes)) ? Number(minutes) : 0;
db.prepare(
"INSERT INTO echonomy_activity_reward_hourly (user_id, hour_start, source, amount, hits, minutes) " +
"INSERT INTO economy_activity_reward_hourly (user_id, hour_start, source, amount, hits, minutes) " +
"VALUES (?, ?, ?, ?, ?, ?) " +
"ON CONFLICT(user_id, hour_start, source) DO UPDATE SET " +
"amount = amount + excluded.amount, " +
@ -1093,7 +1247,7 @@ function flushActivityRewards(db) {
const rows = db
.prepare(
"SELECT user_id, hour_start, source, amount, hits, minutes " +
"FROM echonomy_activity_reward_hourly " +
"FROM economy_activity_reward_hourly " +
"WHERE hour_start < ? " +
"ORDER BY hour_start ASC"
)
@ -1129,7 +1283,7 @@ function flushActivityRewards(db) {
);
if (totalAmount <= 0) {
db.prepare(
"DELETE FROM echonomy_activity_reward_hourly WHERE user_id = ? AND hour_start = ?"
"DELETE FROM economy_activity_reward_hourly WHERE user_id = ? AND hour_start = ?"
).run(group.userId, group.hourStart);
continue;
}
@ -1146,7 +1300,7 @@ function flushActivityRewards(db) {
}
});
db.prepare(
"DELETE FROM echonomy_activity_reward_hourly WHERE user_id = ? AND hour_start = ?"
"DELETE FROM economy_activity_reward_hourly WHERE user_id = ? AND hour_start = ?"
).run(group.userId, group.hourStart);
} catch (error) {
console.error("Failed to apply queued activity reward", error);
@ -1158,7 +1312,8 @@ function registerFramework(api) {
if (!global.lumiFrameworks) {
global.lumiFrameworks = {};
}
global.lumiFrameworks.echonomy = api;
global.lumiFrameworks.economy = api;
global.lumiFrameworks[LEGACY_FRAMEWORK_KEY] = api;
}
function buildApi({ db }) {
@ -1200,7 +1355,7 @@ function registerCommands({ db, settings, commandRouter }) {
const triggers = [config.command.root, ...config.command.aliases];
commandRouter.registerCommands(PLUGIN_ID, [
{
id: "echonomy:root",
id: "economy:root",
triggers,
platforms,
handler: (ctx) => handleCoinsCommand({ ctx, db, settings })
@ -1642,10 +1797,10 @@ function escapeHtml(value) {
}
function installProfileHook(app, getConfig) {
if (!app || app.__echonomyProfileHookInstalled) {
if (!app || app.__economyProfileHookInstalled) {
return;
}
app.__echonomyProfileHookInstalled = true;
app.__economyProfileHookInstalled = true;
const originalRender = app.render.bind(app);
app.render = (view, options, callback) => {
if (typeof options === "function") {
@ -1700,8 +1855,9 @@ async function resolveTargetUser(db, ctx, token) {
}
if (ctx.platform === "discord") {
const message = ctx.meta?.message;
if (message?.mentions?.users?.first) {
const mention = message.mentions.users.first();
const mentionId = token.match(/^<@!?(\d+)>$/)?.[1] || null;
const mention = mentionId ? message?.mentions?.users?.get?.(mentionId) : null;
if (mention?.id) {
const display =
mention.globalName || mention.username || mention.tag || mention.id;
const profile = ensureUserForIdentity({
@ -1714,7 +1870,7 @@ async function resolveTargetUser(db, ctx, token) {
});
return { profile, label: `<@${mention.id}>` };
}
const idMatch = token.match(/^<@!?(\d+)>$/) || token.match(/^(\d{15,})$/);
const idMatch = token.match(/^(\d{15,})$/) || (mentionId ? [token, mentionId] : null);
if (idMatch) {
const profile = ensureUserForIdentity({
provider: "discord",
@ -1735,6 +1891,11 @@ async function resolveTargetUser(db, ctx, token) {
return { profile: internal, label: internal.internal_username };
}
const knownUser = findUserByKnownName(db, cleaned, ctx.platform);
if (knownUser) {
return { profile: knownUser, label: knownUser.internal_username };
}
if (ctx.platform === "twitch") {
const profile = ensureUserForIdentity({
provider: "twitch_login",
@ -1756,7 +1917,7 @@ async function resolveTargetUser(db, ctx, token) {
}
const profile = ensureUserForIdentity({
provider: "echonomy_name",
provider: "economy_name",
providerUserId: cleaned.toLowerCase(),
displayName: cleaned,
fallbackName: cleaned
@ -1770,11 +1931,63 @@ function findUserByInternalName(db, name) {
"SELECT id, internal_username FROM user_profiles WHERE lower(internal_username) = lower(?)"
)
.get(name);
}
}
function findUserByKnownName(db, name, platform) {
const cleaned = (name || "").trim();
if (!cleaned) {
return null;
}
const providerHints = getPlatformProviderHints(platform);
const providerScore = providerHints.length
? `CASE WHEN provider IN (${providerHints.map(() => "?").join(", ")}) THEN 0 ELSE 1 END,`
: "";
const providerParams = providerHints.length ? providerHints : [];
const identity = db
.prepare(
"SELECT user_profiles.id AS id, user_profiles.internal_username AS internal_username " +
"FROM user_identities " +
"JOIN user_profiles ON user_profiles.id = user_identities.user_id " +
"WHERE lower(user_identities.display_name) = lower(?) " +
"OR lower(user_identities.provider_user_id) = lower(?) " +
`ORDER BY ${providerScore} user_identities.updated_at DESC LIMIT 1`
)
.get(cleaned, cleaned, ...providerParams);
if (identity) {
return identity;
}
if (!tableExists(db, "linked_accounts")) {
return null;
}
const linked = db
.prepare(
"SELECT user_profiles.id AS id, user_profiles.internal_username AS internal_username " +
"FROM linked_accounts " +
"JOIN user_profiles ON user_profiles.id = linked_accounts.user_id " +
"WHERE lower(linked_accounts.display_name) = lower(?) " +
"OR lower(linked_accounts.provider_user_id) = lower(?) " +
`ORDER BY ${providerScore.replaceAll("provider", "linked_accounts.provider")} linked_accounts.updated_at DESC LIMIT 1`
)
.get(cleaned, cleaned, ...providerParams);
return linked || null;
}
function getPlatformProviderHints(platform) {
if (platform === "discord") {
return ["discord"];
}
if (platform === "twitch") {
return ["twitch", "twitch_login"];
}
if (platform === "youtube") {
return ["youtube", "youtube_name"];
}
return [];
}
function ensureAccount(db, userId) {
db.prepare(
"INSERT INTO echonomy_accounts (user_id, balance, updated_at) VALUES (?, 0, ?) " +
"INSERT INTO economy_accounts (user_id, balance, updated_at) VALUES (?, 0, ?) " +
"ON CONFLICT(user_id) DO UPDATE SET updated_at = excluded.updated_at"
).run(userId, Date.now());
}
@ -1784,7 +1997,7 @@ function getBalance(db, userId) {
return 0;
}
const row = db
.prepare("SELECT balance FROM echonomy_accounts WHERE user_id = ?")
.prepare("SELECT balance FROM economy_accounts WHERE user_id = ?")
.get(userId);
return row?.balance ?? 0;
}
@ -1792,7 +2005,7 @@ function getBalance(db, userId) {
function updateBalance(db, userId, delta) {
ensureAccount(db, userId);
db.prepare(
"UPDATE echonomy_accounts SET balance = balance + ?, updated_at = ? WHERE user_id = ?"
"UPDATE economy_accounts SET balance = balance + ?, updated_at = ? WHERE user_id = ?"
).run(delta, Date.now(), userId);
}
@ -1838,7 +2051,7 @@ function applyTransaction(db, payload) {
updateBalance(db, toUserId, amount);
}
db.prepare(
"INSERT INTO echonomy_transactions (id, type, amount, from_user_id, to_user_id, note, meta, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
"INSERT INTO economy_transactions (id, type, amount, from_user_id, to_user_id, note, meta, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
).run(
id,
payload.type || "transaction",
@ -1951,7 +2164,7 @@ function listTransactions(db, { userId, limit }) {
return db
.prepare(
"SELECT t.*, fromUser.internal_username AS from_name, toUser.internal_username AS to_name " +
"FROM echonomy_transactions t " +
"FROM economy_transactions t " +
"LEFT JOIN user_profiles AS fromUser ON fromUser.id = t.from_user_id " +
"LEFT JOIN user_profiles AS toUser ON toUser.id = t.to_user_id " +
`${where} ORDER BY t.created_at DESC LIMIT ?`
@ -2013,16 +2226,16 @@ function parseTransactionMeta(rawMeta) {
function buildGlobalStats(db) {
const totalBalance = db
.prepare("SELECT COALESCE(SUM(balance), 0) AS total FROM echonomy_accounts")
.prepare("SELECT COALESCE(SUM(balance), 0) AS total FROM economy_accounts")
.get();
const totalSpent = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE from_user_id IS NOT NULL AND (to_user_id IS NULL OR to_user_id = '')"
)
.get();
const totalTransactions = db
.prepare("SELECT COUNT(*) AS count FROM echonomy_transactions")
.prepare("SELECT COUNT(*) AS count FROM economy_transactions")
.get();
return {
totalBalance: totalBalance?.total || 0,
@ -2044,25 +2257,25 @@ function buildUserStats(db, userId) {
const balance = getBalance(db, userId);
const totalEarned = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE to_user_id = ? AND (from_user_id IS NULL OR from_user_id = '')"
)
.get(userId);
const totalSpent = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE from_user_id = ? AND (to_user_id IS NULL OR to_user_id = '')"
)
.get(userId);
const totalReceived = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE to_user_id = ? AND from_user_id IS NOT NULL AND from_user_id != ''"
)
.get(userId);
const totalSent = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE from_user_id = ? AND to_user_id IS NOT NULL AND to_user_id != ''"
)
.get(userId);
@ -2078,17 +2291,17 @@ function buildUserStats(db, userId) {
function listTopBalances(db, limit) {
return db
.prepare(
"SELECT user_profiles.internal_username AS username, echonomy_accounts.balance AS balance " +
"FROM echonomy_accounts " +
"JOIN user_profiles ON user_profiles.id = echonomy_accounts.user_id " +
"ORDER BY echonomy_accounts.balance DESC LIMIT ?"
"SELECT user_profiles.internal_username AS username, economy_accounts.balance AS balance " +
"FROM economy_accounts " +
"JOIN user_profiles ON user_profiles.id = economy_accounts.user_id " +
"ORDER BY economy_accounts.balance DESC LIMIT ?"
)
.all(limit);
}
function listFunds(db) {
return db
.prepare("SELECT * FROM echonomy_pots WHERE status != 'archived' ORDER BY name")
.prepare("SELECT * FROM economy_pots WHERE status != 'archived' ORDER BY name")
.all();
}
@ -2100,7 +2313,7 @@ function formatProviderLabel(provider) {
twitch_login: "Twitch",
youtube: "YouTube",
youtube_name: "YouTube",
echonomy_name: "Internal"
economy_name: "Internal"
};
if (map[normalized]) {
return map[normalized];
@ -2145,20 +2358,20 @@ function listUserDirectory(db) {
function findFund(db, name) {
return db
.prepare("SELECT * FROM echonomy_pots WHERE lower(name) = lower(?)")
.prepare("SELECT * FROM economy_pots WHERE lower(name) = lower(?)")
.get(name);
}
function createFund(db, { name, description, targetAmount }) {
const now = Date.now();
db.prepare(
"INSERT INTO echonomy_pots (id, name, description, target_amount, current_amount, status, created_at, updated_at) VALUES (?, ?, ?, ?, 0, 'active', ?, ?)"
"INSERT INTO economy_pots (id, name, description, target_amount, current_amount, status, created_at, updated_at) VALUES (?, ?, ?, ?, 0, 'active', ?, ?)"
).run(crypto.randomUUID(), name, description || "", targetAmount || 0, now, now);
}
function updateFund(db, { id, name, description, targetAmount, status }) {
db.prepare(
"UPDATE echonomy_pots SET name = ?, description = ?, target_amount = ?, status = ?, updated_at = ? WHERE id = ?"
"UPDATE economy_pots SET name = ?, description = ?, target_amount = ?, status = ?, updated_at = ? WHERE id = ?"
).run(
name,
description || "",
@ -2173,10 +2386,10 @@ function addFundContribution(db, fundId, userId, amount) {
const now = Date.now();
db.transaction(() => {
db.prepare(
"INSERT INTO echonomy_pot_contributions (id, pot_id, user_id, amount, created_at) VALUES (?, ?, ?, ?, ?)"
"INSERT INTO economy_pot_contributions (id, pot_id, user_id, amount, created_at) VALUES (?, ?, ?, ?, ?)"
).run(crypto.randomUUID(), fundId, userId, amount, now);
db.prepare(
"UPDATE echonomy_pots SET current_amount = current_amount + ?, updated_at = ? WHERE id = ?"
"UPDATE economy_pots SET current_amount = current_amount + ?, updated_at = ? WHERE id = ?"
).run(amount, now, fundId);
})();
}

View File

@ -1,7 +1,7 @@
{
"id": "echonomy-framework",
"id": "economy-framework",
"name": "Economy Framework",
"version": "0.2.7",
"version": "0.2.8",
"description": "Cross-platform currency framework with shared balances and extensible hooks.",
"main": "index.js"
}

View File

@ -3,29 +3,29 @@ function getProfileStats({ db, userId }) {
return { stats: [] };
}
const account = db
.prepare("SELECT balance FROM echonomy_accounts WHERE user_id = ?")
.prepare("SELECT balance FROM economy_accounts WHERE user_id = ?")
.get(userId);
const earned = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE to_user_id = ? AND (from_user_id IS NULL OR from_user_id = '')"
)
.get(userId);
const spent = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE from_user_id = ? AND (to_user_id IS NULL OR to_user_id = '')"
)
.get(userId);
const transfersOut = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE from_user_id = ? AND to_user_id IS NOT NULL AND to_user_id != ''"
)
.get(userId);
const transfersIn = db
.prepare(
"SELECT COALESCE(SUM(amount), 0) AS total FROM echonomy_transactions " +
"SELECT COALESCE(SUM(amount), 0) AS total FROM economy_transactions " +
"WHERE to_user_id = ? AND from_user_id IS NOT NULL AND from_user_id != ''"
)
.get(userId);
@ -44,10 +44,10 @@ function getProfileStats({ db, userId }) {
function getLeaderboards({ db, limit = 10 }) {
const rows = db
.prepare(
"SELECT user_profiles.internal_username AS username, echonomy_accounts.balance AS value " +
"FROM echonomy_accounts " +
"JOIN user_profiles ON user_profiles.id = echonomy_accounts.user_id " +
"ORDER BY echonomy_accounts.balance DESC LIMIT ?"
"SELECT user_profiles.internal_username AS username, economy_accounts.balance AS value " +
"FROM economy_accounts " +
"JOIN user_profiles ON user_profiles.id = economy_accounts.user_id " +
"ORDER BY economy_accounts.balance DESC LIMIT ?"
)
.all(limit);

View File

@ -1,5 +1,5 @@
{
"pluginId": "echonomy-framework",
"pluginId": "economy-framework",
"pluginName": "Economy Framework",
"provider": "stats.js",
"profile": {

View File

@ -1,12 +1,12 @@
<%- include("../../../src/web/views/partials/layout-top", { title }) %>
<style>
.echonomy-grid {
.economy-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.echonomy-card {
.economy-card {
padding: 14px;
border-radius: 14px;
background: var(--surface-2);
@ -14,28 +14,28 @@
flex-direction: column;
gap: 6px;
}
.echonomy-label {
.economy-label {
color: var(--ink-soft);
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.echonomy-value {
.economy-value {
font-size: 1.4rem;
font-weight: 700;
}
.echonomy-currency {
.economy-currency {
display: inline-flex;
align-items: center;
gap: 10px;
}
.echonomy-currency img {
.economy-currency img {
width: 32px;
height: 32px;
border-radius: 8px;
object-fit: cover;
}
.echonomy-list {
.economy-list {
list-style: none;
padding: 0;
margin: 0;
@ -43,7 +43,7 @@
flex-direction: column;
gap: 10px;
}
.echonomy-list li {
.economy-list li {
background: var(--surface-2);
padding: 10px 12px;
border-radius: 12px;
@ -52,7 +52,7 @@
justify-content: space-between;
gap: 10px;
}
.echonomy-table td small {
.economy-table td small {
color: var(--ink-soft);
}
.response-grid {
@ -133,7 +133,7 @@
<div>
<h1>Economy Framework</h1>
<p class="command-subtitle">Unified, cross-platform currency tooling and stats.</p>
<div class="echonomy-currency">
<div class="economy-currency">
<% if (config.currency.icon) { %>
<img src="<%= config.currency.icon %>" alt="Currency icon" />
<% } %>
@ -146,27 +146,27 @@
<section class="card">
<h2>Overview</h2>
<div class="echonomy-grid">
<div class="echonomy-card">
<span class="echonomy-label">Your balance</span>
<span class="echonomy-value"><%= userBalance %></span>
<div class="economy-grid">
<div class="economy-card">
<span class="economy-label">Your balance</span>
<span class="economy-value"><%= userBalance %></span>
</div>
<div class="echonomy-card">
<span class="echonomy-label">Command root</span>
<span class="echonomy-value">!<%= config.command.root %></span>
<div class="economy-card">
<span class="economy-label">Command root</span>
<span class="economy-value">!<%= config.command.root %></span>
</div>
<div class="echonomy-card">
<span class="echonomy-label">Cooldown</span>
<span class="echonomy-value"><%= config.cooldownSeconds %>s</span>
<div class="economy-card">
<span class="economy-label">Cooldown</span>
<span class="economy-value"><%= config.cooldownSeconds %>s</span>
</div>
<% if (isAdmin) { %>
<div class="echonomy-card">
<span class="echonomy-label">Total in circulation</span>
<span class="echonomy-value"><%= globalStats.totalBalance %></span>
<div class="economy-card">
<span class="economy-label">Total in circulation</span>
<span class="economy-value"><%= globalStats.totalBalance %></span>
</div>
<div class="echonomy-card">
<span class="echonomy-label">Total spent</span>
<span class="echonomy-value"><%= globalStats.totalSpent %></span>
<div class="economy-card">
<span class="economy-label">Total spent</span>
<span class="economy-value"><%= globalStats.totalSpent %></span>
</div>
<% } %>
</div>
@ -174,7 +174,7 @@
<% if (isAdmin) { %>
<section class="card">
<h2>Currency settings</h2>
<form method="post" action="/plugins/echonomy-framework/settings/currency" class="form-grid">
<form method="post" action="/plugins/economy-framework/settings/currency" class="form-grid">
<div class="field">
<label>Currency name (singular)</label>
<input name="currency_name" value="<%= config.currency.name %>" />
@ -199,7 +199,7 @@
<section class="card">
<h2>Currency icon</h2>
<form method="post" action="/plugins/echonomy-framework/settings/icon" enctype="multipart/form-data" class="form-grid">
<form method="post" action="/plugins/economy-framework/settings/icon" enctype="multipart/form-data" class="form-grid">
<div class="field">
<label>Upload PNG icon</label>
<input type="file" name="currency_icon" accept="image/png" />
@ -211,7 +211,7 @@
<section class="card">
<h2>Banking labels</h2>
<form method="post" action="/plugins/echonomy-framework/settings/banking" class="form-grid">
<form method="post" action="/plugins/economy-framework/settings/banking" class="form-grid">
<div class="field">
<label>Banking page label</label>
<input name="banking_label" value="<%= config.banking.label %>" />
@ -244,7 +244,7 @@
<section class="card">
<h2>Platforms</h2>
<form method="post" action="/plugins/echonomy-framework/settings/platforms" class="form-grid">
<form method="post" action="/plugins/economy-framework/settings/platforms" class="form-grid">
<div class="field">
<label>Enable on Discord</label>
<label class="switch">
@ -290,7 +290,7 @@
<section class="card">
<h2>Currency earning rules</h2>
<form method="post" action="/plugins/echonomy-framework/settings/earn" class="form-grid">
<form method="post" action="/plugins/economy-framework/settings/earn" class="form-grid">
<div class="field">
<label>Discord message rewards</label>
<label class="switch">
@ -360,7 +360,7 @@
<section class="card">
<h2>Monetization tiers</h2>
<form method="post" action="/plugins/echonomy-framework/settings/tiers" class="form-grid">
<form method="post" action="/plugins/economy-framework/settings/tiers" class="form-grid">
<div class="field">
<label>Discord server booster multiplier</label>
<input name="tier_discord_booster_multiplier" value="<%= config.tiers.discordBooster %>" />
@ -390,7 +390,7 @@
<% if (!funds.length) { %>
<p>No <%= config.communityFunds.plural.toLowerCase() %> configured yet.</p>
<% } else { %>
<ul class="echonomy-list">
<ul class="economy-list">
<% funds.forEach((fund) => { %>
<li>
<span><strong><%= fund.name %></strong> - <%= fund.current_amount %>/<%= fund.target_amount %></span>
@ -401,7 +401,7 @@
<% } %>
<% if (isAdmin) { %>
<h3>Create <%= config.communityFunds.name %></h3>
<form method="post" action="/plugins/echonomy-framework/funds/create" class="form-grid">
<form method="post" action="/plugins/economy-framework/funds/create" class="form-grid">
<div class="field">
<label>Name</label>
<input name="name" />
@ -419,7 +419,7 @@
<h3>Update <%= config.communityFunds.plural %></h3>
<% funds.forEach((fund) => { %>
<form method="post" action="/plugins/echonomy-framework/funds/<%= fund.id %>/update" class="form-grid">
<form method="post" action="/plugins/economy-framework/funds/<%= fund.id %>/update" class="form-grid">
<div class="field">
<label>Name</label>
<input name="name" value="<%= fund.name %>" />
@ -450,18 +450,18 @@
<% if (!events.length) { %>
<p>No custom events configured yet.</p>
<% } else { %>
<ul class="echonomy-list">
<ul class="economy-list">
<% events.forEach((event) => { %>
<li>
<span><strong><%= event.name %></strong> (<%= event.amount %>)</span>
<form method="post" action="/plugins/echonomy-framework/events/<%= event.id %>/delete">
<form method="post" action="/plugins/economy-framework/events/<%= event.id %>/delete">
<button type="submit" class="button subtle">Delete</button>
</form>
</li>
<% }) %>
</ul>
<% } %>
<form method="post" action="/plugins/echonomy-framework/events/create" class="form-grid">
<form method="post" action="/plugins/economy-framework/events/create" class="form-grid">
<div class="field">
<label>Event name</label>
<input name="name" />
@ -502,7 +502,7 @@
<h3>Edit: <%= response.label %></h3>
<button type="button" class="button subtle" data-modal-close>Close</button>
</div>
<form method="post" action="/plugins/echonomy-framework/settings/responses" data-response-form>
<form method="post" action="/plugins/economy-framework/settings/responses" data-response-form>
<input type="hidden" name="response_key" value="<%= response.key %>" />
<div class="field">
<label>Selection mode</label>
@ -536,7 +536,7 @@
<% if (isMod) { %>
<section class="card">
<h2>Adjust user balance</h2>
<form method="post" action="/plugins/echonomy-framework/accounts/adjust" class="form-grid">
<form method="post" action="/plugins/economy-framework/accounts/adjust" class="form-grid">
<div class="field">
<label>Username</label>
<input name="username" />
@ -594,12 +594,12 @@
type="search"
placeholder="Search transactions"
aria-label="Search transactions"
data-table-filter="echonomy-transactions"
data-table-filter="economy-transactions"
/>
<div class="table-controls">
<label class="table-page-size">
Show
<select data-table-size="echonomy-transactions">
<select data-table-size="economy-transactions">
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
@ -611,8 +611,8 @@
</div>
<div class="table-wrap">
<table
class="table echonomy-table"
data-table="echonomy-transactions"
class="table economy-table"
data-table="economy-transactions"
data-pageable="true"
data-page-size="25"
data-page-sizes="25,50,100,250"
@ -680,7 +680,7 @@
</tbody>
</table>
</div>
<div class="table-pagination" data-table-pagination="echonomy-transactions">
<div class="table-pagination" data-table-pagination="economy-transactions">
<button type="button" class="button subtle" data-page-prev>Previous</button>
<span class="table-page-label" data-page-label>Page 1 of 1</span>
<button type="button" class="button subtle" data-page-next>Next</button>

View File

@ -1,5 +1,5 @@
{
"pluginId": "echonomy-games",
"pluginId": "economy-games",
"pluginName": "Economy Games",
"commands": [
{

View File

@ -1,7 +1,7 @@
{
"id": "echonomy-games",
"id": "economy-games",
"name": "Economy Games",
"version": "0.1.6",
"version": "0.1.7",
"description": "Cross-platform mini-games that use the Economy currency framework.",
"main": "index.js"
}

View File

@ -214,7 +214,7 @@
</summary>
<div class="game-row__body">
<section class="game-card">
<form method="post" action="/plugins/echonomy-games/settings/hotpotato" class="form-grid">
<form method="post" action="/plugins/economy-games/settings/hotpotato" class="form-grid">
<div class="field">
<label>Enable Hot Potato</label>
<label class="switch">
@ -315,7 +315,7 @@
<summary>Edit replies</summary>
<div class="reply-body">
<p class="hint">One reply per line. Tokens: {user}, {target}, {amount}, {payout}, {seconds}, {min}, {max}, {game}, {holder}, {loss}, {winners}, {trigger}.</p>
<form method="post" action="/plugins/echonomy-games/settings/responses" class="form-grid responses-grid">
<form method="post" action="/plugins/economy-games/settings/responses" class="form-grid responses-grid">
<% responsesByGame.hotpotato.forEach((response) => { %>
<div class="field response-field">
<label><%= response.label %></label>
@ -359,7 +359,7 @@
</summary>
<div class="game-row__body">
<section class="game-card">
<form method="post" action="/plugins/echonomy-games/settings/coinflip" class="form-grid">
<form method="post" action="/plugins/economy-games/settings/coinflip" class="form-grid">
<div class="field">
<label>Enable Coinflip</label>
<label class="switch">
@ -447,7 +447,7 @@
<summary>Edit replies</summary>
<div class="reply-body">
<p class="hint">One reply per line. Tokens: {user}, {amount}, {payout}, {min}, {max}, {seconds}.</p>
<form method="post" action="/plugins/echonomy-games/settings/responses" class="form-grid responses-grid">
<form method="post" action="/plugins/economy-games/settings/responses" class="form-grid responses-grid">
<% responsesByGame.coinflip.forEach((response) => { %>
<div class="field response-field">
<label><%= response.label %></label>
@ -491,7 +491,7 @@
</summary>
<div class="game-row__body">
<section class="game-card">
<form method="post" action="/plugins/echonomy-games/settings/mystery" class="form-grid">
<form method="post" action="/plugins/economy-games/settings/mystery" class="form-grid">
<div class="field">
<label>Enable Mystery Box</label>
<label class="switch">
@ -579,7 +579,7 @@
<summary>Edit replies</summary>
<div class="reply-body">
<p class="hint">One reply per line. Tokens: {user}, {amount}, {payout}, {min}, {max}, {seconds}.</p>
<form method="post" action="/plugins/echonomy-games/settings/responses" class="form-grid responses-grid">
<form method="post" action="/plugins/economy-games/settings/responses" class="form-grid responses-grid">
<% responsesByGame.mystery.forEach((response) => { %>
<div class="field response-field">
<label><%= response.label %></label>

View File

@ -12,7 +12,7 @@ const ROUTE_ALIASES = Object.freeze({
"/admin/youtube-wizard": "youtube configuration config settings wizard location where integration",
"/admin/plugins": "plugin plugins settings configuration management",
"/admin/commands": "command commands custom command settings management",
"/plugins/echonomy-framework": "economy echonomy currency points banking settings",
"/plugins/economy-framework": "economy currency points banking settings",
"/plugins/moderation": "moderation mod tools sanctions bans timeouts settings"
});

View File

@ -944,7 +944,7 @@ async function tryWhisper(client, username, message) {
}
function distributeBanAssets(db, subjectId, { reason }) {
const framework = global.lumiFrameworks?.echonomy;
const framework = global.lumiFrameworks?.economy;
if (!framework) {
return;
}

View File

@ -1,7 +1,7 @@
{
"id": "moderation",
"name": "Moderation Center",
"version": "0.1.3",
"version": "0.1.4",
"description": "Cross-platform moderation actions, notes, and sanctions.",
"main": "index.js"
}

View File

@ -5,6 +5,17 @@ const { db } = require("./db");
const pluginsDir = path.join(__dirname, "..", "..", "plugins");
const cleanupHandlers = [];
const legacyEconomyStem = ["echo", "nomy"].join("");
const economyPluginAliases = {
"economy-framework": [`${legacyEconomyStem}-framework`],
"economy-games": [`${legacyEconomyStem}-games`]
};
const economyCommandAliases = [
{ from: `${legacyEconomyStem}:root`, to: "economy:root" },
{ from: `${legacyEconomyStem}-games:hotpotato`, to: "economy-games:hotpotato" },
{ from: `${legacyEconomyStem}-games:coinflip`, to: "economy-games:coinflip" },
{ from: `${legacyEconomyStem}-games:mystery`, to: "economy-games:mystery" }
];
function readJson(filePath) {
const raw = fs.readFileSync(filePath, "utf8");
@ -36,7 +47,8 @@ function scanPluginDirectories() {
version: manifest.version || "0.0.0",
description: manifest.description || "",
main: manifest.main || "index.js",
dir: path.join(pluginsDir, entry.name)
dir: path.join(pluginsDir, entry.name),
legacyIds: economyPluginAliases[manifest.id] || []
});
} catch {
continue;
@ -55,6 +67,7 @@ function syncPluginRegistry() {
);
for (const plugin of plugins) {
migratePluginAliases(plugin, now);
insert.run(
plugin.id,
plugin.name,
@ -65,9 +78,70 @@ function syncPluginRegistry() {
now
);
}
migrateCommandUsageAliases();
return plugins;
}
function migratePluginAliases(plugin, now) {
for (const legacyId of plugin.legacyIds || []) {
const existing = db.prepare("SELECT * FROM plugins WHERE id = ?").get(plugin.id);
const legacy = db.prepare("SELECT * FROM plugins WHERE id = ?").get(legacyId);
if (legacy && !existing) {
db.prepare(
"INSERT INTO plugins (id, name, version, enabled, source, path, installed_at, updated_at) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
).run(
plugin.id,
plugin.name,
plugin.version,
legacy.enabled,
legacy.source || "local",
plugin.dir,
legacy.installed_at || now,
now
);
} else if (legacy && existing) {
db.prepare("UPDATE plugins SET enabled = ?, updated_at = ? WHERE id = ?").run(
legacy.enabled,
now,
plugin.id
);
}
const settingsRows = db
.prepare("SELECT key, value, updated_at FROM plugin_settings WHERE plugin_id = ?")
.all(legacyId);
const copySetting = db.prepare(
"INSERT OR IGNORE INTO plugin_settings (plugin_id, key, value, updated_at) VALUES (?, ?, ?, ?)"
);
for (const row of settingsRows) {
copySetting.run(plugin.id, row.key, row.value, row.updated_at || now);
}
db.prepare("DELETE FROM plugin_settings WHERE plugin_id = ?").run(legacyId);
db.prepare("DELETE FROM plugins WHERE id = ?").run(legacyId);
}
}
function migrateCommandUsageAliases() {
const table = db
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'command_usage'")
.get();
if (!table) {
return;
}
for (const alias of economyCommandAliases) {
const oldRow = db.prepare("SELECT count, updated_at FROM command_usage WHERE command_id = ?").get(alias.from);
if (!oldRow) {
continue;
}
db.prepare(
"INSERT INTO command_usage (command_id, count, updated_at) VALUES (?, ?, ?) " +
"ON CONFLICT(command_id) DO UPDATE SET count = command_usage.count + excluded.count, updated_at = MAX(command_usage.updated_at, excluded.updated_at)"
).run(alias.to, oldRow.count || 0, oldRow.updated_at || Date.now());
db.prepare("DELETE FROM command_usage WHERE command_id = ?").run(alias.from);
}
}
function getPlugins() {
return db.prepare("SELECT * FROM plugins ORDER BY name").all();
}

View File

@ -422,15 +422,15 @@ function buildCommandUsageRows(limit) {
}
function buildCurrencyRows(limit) {
if (!tableExists("echonomy_accounts")) {
if (!tableExists("economy_accounts")) {
return { rows: [], emptyMessage: "Currency framework not active." };
}
const rows = db
.prepare(
"SELECT user_profiles.internal_username AS username, echonomy_accounts.balance AS value " +
"FROM echonomy_accounts " +
"JOIN user_profiles ON user_profiles.id = echonomy_accounts.user_id " +
"ORDER BY echonomy_accounts.balance DESC LIMIT ?"
"SELECT user_profiles.internal_username AS username, economy_accounts.balance AS value " +
"FROM economy_accounts " +
"JOIN user_profiles ON user_profiles.id = economy_accounts.user_id " +
"ORDER BY economy_accounts.balance DESC LIMIT ?"
)
.all(limit);
return { rows, valueLabel: getCurrencyLabel() };
@ -494,11 +494,11 @@ function buildCommandIndex() {
if (command.id && !command.id.toString().includes(":")) {
ids.push(`${plugin.id}:${command.id}`);
}
if (plugin.id === "echonomy-framework" && command.id === "root") {
ids.push("echonomy:root");
if (plugin.id === "economy-framework" && command.id === "root") {
ids.push("economy:root");
}
if (plugin.id === "echonomy-games" && command.id === "mysterybox") {
ids.push("echonomy-games:mystery");
if (plugin.id === "economy-games" && command.id === "mysterybox") {
ids.push("economy-games:mystery");
}
const label = subcommand ? `${prefix}${trigger} ${subcommand}` : `${prefix}${trigger}`;
addEntry(ids, label, commandId);
@ -588,8 +588,8 @@ function formatCommandId(commandId, prefix = "!") {
}
function getCurrencyLabel() {
const plural = getPluginSetting("echonomy-framework", "currency_name_plural");
const singular = getPluginSetting("echonomy-framework", "currency_name");
const plural = getPluginSetting("economy-framework", "currency_name_plural");
const singular = getPluginSetting("economy-framework", "currency_name");
return plural || singular || "Coins";
}