From 580b4392b4c36ab241a6427b0e9f141b3eb7cc3f Mon Sep 17 00:00:00 2001 From: Franz Rolfsvaag Date: Wed, 17 Jun 2026 22:32:51 +0200 Subject: [PATCH] rename economy plugins and migrate legacy ids --- TODO.md | 1 + package-lock.json | 4 +- package.json | 2 +- plugins/birthday/index.js | 4 +- plugins/birthday/plugin.json | 2 +- plugins/birthday/views/birthday-admin.ejs | 2 +- .../cmds.json | 2 +- .../index.js | 313 ++- .../plugin.json | 4 +- .../stats.js | 18 +- .../stats.json | 2 +- .../test.txt | 0 .../views/banking.ejs | 0 .../views/economy.ejs} | 90 +- .../cmds.json | 2 +- .../index.js | 1924 +++++++++-------- .../plugin.json | 4 +- .../views/games.ejs | 12 +- plugins/lumi_ai/backend/repo_indexer.js | 2 +- plugins/moderation/index.js | 2 +- plugins/moderation/plugin.json | 2 +- src/services/plugins.js | 76 +- src/services/top.js | 22 +- 23 files changed, 1430 insertions(+), 1060 deletions(-) rename plugins/{echonomy-framework => economy-framework}/cmds.json (98%) rename plugins/{echonomy-framework => economy-framework}/index.js (86%) rename plugins/{echonomy-framework => economy-framework}/plugin.json (74%) rename plugins/{echonomy-framework => economy-framework}/stats.js (66%) rename plugins/{echonomy-framework => economy-framework}/stats.json (87%) rename plugins/{echonomy-framework => economy-framework}/test.txt (100%) rename plugins/{echonomy-framework => economy-framework}/views/banking.ejs (100%) rename plugins/{echonomy-framework/views/echonomy.ejs => economy-framework/views/economy.ejs} (89%) rename plugins/{echonomy-games => economy-games}/cmds.json (96%) rename plugins/{echonomy-games => economy-games}/index.js (91%) rename plugins/{echonomy-games => economy-games}/plugin.json (74%) rename plugins/{echonomy-games => economy-games}/views/games.ejs (97%) diff --git a/TODO.md b/TODO.md index bda54d8..c00219d 100644 --- a/TODO.md +++ b/TODO.md @@ -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. diff --git a/package-lock.json b/package-lock.json index 7c69903..0a7936a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 8e315b5..c82a39f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lumi-bot", - "version": "0.1.6", + "version": "0.1.7", "private": true, "type": "commonjs", "scripts": { diff --git a/plugins/birthday/index.js b/plugins/birthday/index.js index 37c62a3..e55a656 100644 --- a/plugins/birthday/index.js +++ b/plugins/birthday/index.js @@ -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) }; } diff --git a/plugins/birthday/plugin.json b/plugins/birthday/plugin.json index d8d1cd5..13acc92 100644 --- a/plugins/birthday/plugin.json +++ b/plugins/birthday/plugin.json @@ -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" } diff --git a/plugins/birthday/views/birthday-admin.ejs b/plugins/birthday/views/birthday-admin.ejs index 6c3a378..eeb7a3f 100644 --- a/plugins/birthday/views/birthday-admin.ejs +++ b/plugins/birthday/views/birthday-admin.ejs @@ -12,7 +12,7 @@ Discord client<%= diagnostics.discordAvailable ? (diagnostics.discordReady ? "Ready" : "Available") : "Unavailable" %> Announcement channel<%= diagnostics.channel.message %> - Economy<%= diagnostics.echonomyAvailable ? "Available" : "Unavailable" %> + Economy<%= diagnostics.economyAvailable ? "Available" : "Unavailable" %> Current plugin date<%= diagnostics.currentDate.year %>-<%= String(diagnostics.currentDate.month).padStart(2, "0") %>-<%= String(diagnostics.currentDate.day).padStart(2, "0") %> diff --git a/plugins/echonomy-framework/cmds.json b/plugins/economy-framework/cmds.json similarity index 98% rename from plugins/echonomy-framework/cmds.json rename to plugins/economy-framework/cmds.json index a9a1660..aea57ed 100644 --- a/plugins/echonomy-framework/cmds.json +++ b/plugins/economy-framework/cmds.json @@ -1,5 +1,5 @@ { - "pluginId": "echonomy-framework", + "pluginId": "economy-framework", "pluginName": "Economy Framework", "platformKeys": { "discord": "platform_discord", diff --git a/plugins/echonomy-framework/index.js b/plugins/economy-framework/index.js similarity index 86% rename from plugins/echonomy-framework/index.js rename to plugins/economy-framework/index.js index bee8caf..40a00e4 100644 --- a/plugins/echonomy-framework/index.js +++ b/plugins/economy-framework/index.js @@ -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); })(); } diff --git a/plugins/echonomy-framework/plugin.json b/plugins/economy-framework/plugin.json similarity index 74% rename from plugins/echonomy-framework/plugin.json rename to plugins/economy-framework/plugin.json index 89f414a..de31121 100644 --- a/plugins/echonomy-framework/plugin.json +++ b/plugins/economy-framework/plugin.json @@ -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" } diff --git a/plugins/echonomy-framework/stats.js b/plugins/economy-framework/stats.js similarity index 66% rename from plugins/echonomy-framework/stats.js rename to plugins/economy-framework/stats.js index caf644b..2924015 100644 --- a/plugins/echonomy-framework/stats.js +++ b/plugins/economy-framework/stats.js @@ -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); diff --git a/plugins/echonomy-framework/stats.json b/plugins/economy-framework/stats.json similarity index 87% rename from plugins/echonomy-framework/stats.json rename to plugins/economy-framework/stats.json index d082c89..220a395 100644 --- a/plugins/echonomy-framework/stats.json +++ b/plugins/economy-framework/stats.json @@ -1,5 +1,5 @@ { - "pluginId": "echonomy-framework", + "pluginId": "economy-framework", "pluginName": "Economy Framework", "provider": "stats.js", "profile": { diff --git a/plugins/echonomy-framework/test.txt b/plugins/economy-framework/test.txt similarity index 100% rename from plugins/echonomy-framework/test.txt rename to plugins/economy-framework/test.txt diff --git a/plugins/echonomy-framework/views/banking.ejs b/plugins/economy-framework/views/banking.ejs similarity index 100% rename from plugins/echonomy-framework/views/banking.ejs rename to plugins/economy-framework/views/banking.ejs diff --git a/plugins/echonomy-framework/views/echonomy.ejs b/plugins/economy-framework/views/economy.ejs similarity index 89% rename from plugins/echonomy-framework/views/echonomy.ejs rename to plugins/economy-framework/views/economy.ejs index 205e812..522a2b5 100644 --- a/plugins/echonomy-framework/views/echonomy.ejs +++ b/plugins/economy-framework/views/economy.ejs @@ -1,12 +1,12 @@ <%- include("../../../src/web/views/partials/layout-top", { title }) %>