rename economy plugins and migrate legacy ids
This commit is contained in:
parent
471218a79d
commit
580b4392b4
1
TODO.md
1
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.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lumi-bot",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
|
||||
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"pluginId": "echonomy-framework",
|
||||
"pluginId": "economy-framework",
|
||||
"pluginName": "Economy Framework",
|
||||
"platformKeys": {
|
||||
"discord": "platform_discord",
|
||||
@ -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
|
||||
@ -1772,9 +1933,61 @@ function findUserByInternalName(db, name) {
|
||||
.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);
|
||||
})();
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"pluginId": "echonomy-framework",
|
||||
"pluginId": "economy-framework",
|
||||
"pluginName": "Economy Framework",
|
||||
"provider": "stats.js",
|
||||
"profile": {
|
||||
@ -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>
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"pluginId": "echonomy-games",
|
||||
"pluginId": "economy-games",
|
||||
"pluginName": "Economy Games",
|
||||
"commands": [
|
||||
{
|
||||
@ -2,7 +2,10 @@ const path = require("path");
|
||||
const crypto = require("crypto");
|
||||
const { ensureUserForIdentity } = require("../../src/services/users");
|
||||
|
||||
const PLUGIN_ID = "echonomy-games";
|
||||
|
||||
const PLUGIN_ID = "economy-games";
|
||||
const LEGACY_STEM = ["echo", "nomy"].join("");
|
||||
const LEGACY_PLUGIN_ID = `${LEGACY_STEM}-games`;
|
||||
const DEFAULT_SETTINGS = {
|
||||
hotpotato_enabled: "1",
|
||||
hotpotato_platform_discord: "1",
|
||||
@ -111,6 +114,7 @@ let cachedConfigAt = 0;
|
||||
module.exports = {
|
||||
id: PLUGIN_ID,
|
||||
init({ web, settings, db, commandRouter, discordClient, twitchClient }) {
|
||||
migrateLegacyInstall(db);
|
||||
ensureDefaults(db);
|
||||
ensureStatsTable(db);
|
||||
const refreshCommands = registerCommands({ db, commandRouter, settings });
|
||||
@ -335,6 +339,7 @@ module.exports = {
|
||||
role: "admin",
|
||||
section: "plugins"
|
||||
});
|
||||
mountLegacyRedirect(web, LEGACY_PLUGIN_ID, PLUGIN_ID);
|
||||
}
|
||||
};
|
||||
|
||||
@ -345,6 +350,82 @@ function deny(res) {
|
||||
});
|
||||
}
|
||||
|
||||
function migrateLegacyInstall(db) {
|
||||
migratePluginSettings(db);
|
||||
mergeCommandUsage(db, `${LEGACY_PLUGIN_ID}:hotpotato`, `${PLUGIN_ID}:hotpotato`);
|
||||
mergeCommandUsage(db, `${LEGACY_PLUGIN_ID}:coinflip`, `${PLUGIN_ID}:coinflip`);
|
||||
mergeCommandUsage(db, `${LEGACY_PLUGIN_ID}:mystery`, `${PLUGIN_ID}:mystery`);
|
||||
}
|
||||
|
||||
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 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 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)) {
|
||||
@ -506,8 +587,9 @@ function loadCustomResponses(db) {
|
||||
}
|
||||
|
||||
function ensureStatsTable(db) {
|
||||
migrateTable(db, `${LEGACY_STEM}_game_stats`, "economy_game_stats");
|
||||
db.prepare(
|
||||
"CREATE TABLE IF NOT EXISTS echonomy_game_stats (" +
|
||||
"CREATE TABLE IF NOT EXISTS economy_game_stats (" +
|
||||
"game_key TEXT PRIMARY KEY," +
|
||||
"plays INTEGER NOT NULL DEFAULT 0," +
|
||||
"coins_won INTEGER NOT NULL DEFAULT 0," +
|
||||
@ -523,7 +605,7 @@ function getGameStats(db, gameKey) {
|
||||
return db
|
||||
.prepare(
|
||||
"SELECT game_key, plays, coins_won, coins_lost, last_played_at, last_played_user_id, last_played_username " +
|
||||
"FROM echonomy_game_stats WHERE game_key = ?"
|
||||
"FROM economy_game_stats WHERE game_key = ?"
|
||||
)
|
||||
.get(gameKey);
|
||||
}
|
||||
@ -553,7 +635,7 @@ function getGameStatsView(db, gameKey) {
|
||||
function recordGamePlay(db, gameKey, { userId, username }) {
|
||||
const now = Date.now();
|
||||
db.prepare(
|
||||
"INSERT INTO echonomy_game_stats " +
|
||||
"INSERT INTO economy_game_stats " +
|
||||
"(game_key, plays, coins_won, coins_lost, last_played_at, last_played_user_id, last_played_username) " +
|
||||
"VALUES (?, 1, 0, 0, ?, ?, ?) " +
|
||||
"ON CONFLICT(game_key) DO UPDATE SET " +
|
||||
@ -571,7 +653,7 @@ function recordGameTotals(db, gameKey, { coinsWon = 0, coinsLost = 0 }) {
|
||||
return;
|
||||
}
|
||||
db.prepare(
|
||||
"INSERT INTO echonomy_game_stats (game_key, plays, coins_won, coins_lost) " +
|
||||
"INSERT INTO economy_game_stats (game_key, plays, coins_won, coins_lost) " +
|
||||
"VALUES (?, 0, ?, ?) " +
|
||||
"ON CONFLICT(game_key) DO UPDATE SET " +
|
||||
"coins_won = coins_won + ?, " +
|
||||
@ -610,7 +692,7 @@ function toLabel(key) {
|
||||
}
|
||||
|
||||
function getFramework() {
|
||||
return global.lumiFrameworks?.echonomy || null;
|
||||
return global.lumiFrameworks?.economy || global.lumiFrameworks?.[LEGACY_STEM] || null;
|
||||
}
|
||||
|
||||
function registerCommands({ db, commandRouter, settings }) {
|
||||
@ -626,7 +708,7 @@ function registerCommands({ db, commandRouter, settings }) {
|
||||
const platforms = platformsFromConfig(config.hotpotato.platforms);
|
||||
if (platforms.length && triggers.length) {
|
||||
commands.push({
|
||||
id: "echonomy-games:hotpotato",
|
||||
id: "economy-games:hotpotato",
|
||||
triggers,
|
||||
platforms,
|
||||
handler: (ctx) => handleHotPotato({ ctx, db, settings })
|
||||
@ -639,7 +721,7 @@ function registerCommands({ db, commandRouter, settings }) {
|
||||
const platforms = platformsFromConfig(config.coinflip.platforms);
|
||||
if (platforms.length && triggers.length) {
|
||||
commands.push({
|
||||
id: "echonomy-games:coinflip",
|
||||
id: "economy-games:coinflip",
|
||||
triggers,
|
||||
platforms,
|
||||
handler: (ctx) => handleCoinflip({ ctx, db, settings })
|
||||
@ -652,7 +734,7 @@ function registerCommands({ db, commandRouter, settings }) {
|
||||
const platforms = platformsFromConfig(config.mystery.platforms);
|
||||
if (platforms.length && triggers.length) {
|
||||
commands.push({
|
||||
id: "echonomy-games:mystery",
|
||||
id: "economy-games:mystery",
|
||||
triggers,
|
||||
platforms,
|
||||
handler: (ctx) => handleMystery({ ctx, db, settings })
|
||||
@ -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"
|
||||
}
|
||||
@ -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>
|
||||
@ -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"
|
||||
});
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user