mirror of
https://github.com/LBRYFoundation/curate.git
synced 2025-08-23 17:37:25 +00:00
Add admin commands
This commit is contained in:
parent
d09c8fa311
commit
2c156c6853
11 changed files with 526 additions and 0 deletions
67
src/commands/admin/abandonall.ts
Normal file
67
src/commands/admin/abandonall.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { oneLine } from 'common-tags';
|
||||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { confirm, resolveUser } from '../../util';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class AbandonAllCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'abandonall',
|
||||
description: 'Abandons all supports of all accounts or of a given account.',
|
||||
category: 'Admin',
|
||||
aliases: ['abanall', 'dropall'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['abandonall', 'abandonall @user'],
|
||||
usage: '[user]'
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
// Single user abandon
|
||||
if (ctx.args[0]) {
|
||||
const discordID = resolveUser(ctx.args[0]);
|
||||
if (!discordID) return "That Discord user isn't valid.";
|
||||
|
||||
const account = await this.lbryx.ensureAccount(discordID, false);
|
||||
if (!account.id) return 'That user does not have an account.';
|
||||
|
||||
const supportsCount = await this.lbryx.getSupportsCount(account.id);
|
||||
if (supportsCount <= 0) return 'That user does not have any supports.';
|
||||
|
||||
if (
|
||||
!(await confirm(
|
||||
ctx,
|
||||
oneLine`
|
||||
Are you sure you want to abandon **all supports** from that account?
|
||||
*(${supportsCount.toLocaleString()} support[s])*
|
||||
`
|
||||
))
|
||||
)
|
||||
return;
|
||||
await this.lbryx.abandonAllClaims(account.id);
|
||||
return `Abandoned ${supportsCount.toLocaleString()} support(s).`;
|
||||
}
|
||||
|
||||
// Abandon ALL supports
|
||||
|
||||
await this.lbryx.sync();
|
||||
const pairs = this.lbryx.getIDs();
|
||||
if (pairs.length <= 0) return 'No pairs in the database.';
|
||||
|
||||
if (!(await confirm(ctx, 'Are you sure you want to abandon **all supports** from **all accounts**?')))
|
||||
return;
|
||||
|
||||
await this.client.startTyping(ctx.channel.id);
|
||||
let count = 0;
|
||||
for (const [, lbryID] of pairs) {
|
||||
const result = await this.lbryx.abandonAllClaims(lbryID);
|
||||
if (result) count += result.count;
|
||||
}
|
||||
this.client.stopTyping(ctx.channel.id);
|
||||
return `Abandoned ${count.toLocaleString()} supports(s).`;
|
||||
}
|
||||
}
|
24
src/commands/admin/adminbalance.ts
Normal file
24
src/commands/admin/adminbalance.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { DexareClient } from 'dexare';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class AdminBalanceCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'adminbalance',
|
||||
description: 'Shows the master wallet balance.',
|
||||
category: 'Admin',
|
||||
aliases: ['abal', 'adminbal'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['adminbalance']
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run() {
|
||||
const wallet = await this.lbry.walletBalance();
|
||||
return this.displayWallet(wallet, 'Master Wallet Balance');
|
||||
}
|
||||
}
|
71
src/commands/admin/allsupports.ts
Normal file
71
src/commands/admin/allsupports.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { Support } from '../../modules/lbry/types';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
import { paginate } from '../../util/pager';
|
||||
|
||||
export default class AllSupportsommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'allsupports',
|
||||
description: 'List all supports from all users.',
|
||||
category: 'Admin',
|
||||
aliases: ['asups', 'allsups'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['allsupports', 'allsupports @channel#a/video#b'],
|
||||
usage: '[claim]'
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
let claim: string | null = null;
|
||||
if (ctx.args[0]) {
|
||||
claim = await this.lbryx.resolveClaim(ctx.args[0]);
|
||||
if (!claim) return "That claim isn't valid.";
|
||||
}
|
||||
|
||||
await this.lbryx.sync();
|
||||
const pairs = this.lbryx.getIDs();
|
||||
if (pairs.length <= 0) return 'No users found in the database.';
|
||||
|
||||
const allSupports: (Support & {
|
||||
discordID: string;
|
||||
accountID: string;
|
||||
})[] = [];
|
||||
|
||||
for (const [discordID, accountID] of pairs) {
|
||||
const supportsCount = await this.lbryx.getSupportsCount(accountID);
|
||||
if (supportsCount <= 0) continue;
|
||||
const supports = await this.lbry.supportList({
|
||||
account_id: accountID,
|
||||
page_size: supportsCount,
|
||||
claim_id: claim || undefined
|
||||
});
|
||||
for (const support of supports.items)
|
||||
allSupports.push({
|
||||
...support,
|
||||
discordID,
|
||||
accountID
|
||||
});
|
||||
}
|
||||
|
||||
if (allSupports.length <= 0) return 'No supports found.';
|
||||
|
||||
await paginate(
|
||||
ctx,
|
||||
{
|
||||
title: 'Supports',
|
||||
items: allSupports.map(
|
||||
(item) => `> ${item.name} \`${item.claim_id}\`\n> <@${item.discordID}> ${item.amount} LBC`
|
||||
),
|
||||
itemSeparator: '\n\n'
|
||||
},
|
||||
{
|
||||
author: { name: `All supports ${claim ? ` on claim \`${claim}\`` : ''}` }
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
48
src/commands/admin/deleteaccount.ts
Normal file
48
src/commands/admin/deleteaccount.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { stripIndents } from 'common-tags';
|
||||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { confirm, resolveUser } from '../../util';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class DeleteAccountommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'deleteaccount',
|
||||
description: "Delete a user's Curation account.",
|
||||
category: 'Admin',
|
||||
aliases: ['del', 'delacc'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['deleteaccount @user'],
|
||||
usage: '<user>'
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
const discordID = resolveUser(ctx.args[0]);
|
||||
if (!discordID) return "That Discord user isn't valid.";
|
||||
const account = await this.lbryx.ensureAccount(discordID, false);
|
||||
if (!account.id) return 'That user does not have an account.';
|
||||
|
||||
const supportsCount = await this.lbryx.getSupportsCount(account.id);
|
||||
if (
|
||||
!(await confirm(
|
||||
ctx,
|
||||
`Are you sure you want to delete that account? *(${supportsCount.toLocaleString()} support[s])*`
|
||||
))
|
||||
)
|
||||
return;
|
||||
|
||||
try {
|
||||
await this.lbryx.deleteAccount(discordID, account.id);
|
||||
return 'Deleted account.';
|
||||
} catch (e) {
|
||||
return stripIndents`
|
||||
Failed to delete the account. An error most likely occured while backing up the wallet.
|
||||
\`\`\`\n${e.toString()}\`\`\`
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
47
src/commands/admin/deleteall.ts
Normal file
47
src/commands/admin/deleteall.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { stripIndents } from 'common-tags';
|
||||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { confirm } from '../../util';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class DeleteAllCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'deleteall',
|
||||
description: 'Deletes all accounts in the database.',
|
||||
category: 'Admin',
|
||||
aliases: ['delall'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['delall']
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
await this.lbryx.sync();
|
||||
const pairs = this.lbryx.getIDs();
|
||||
|
||||
if (pairs.length <= 0) return 'No pairs in the database.';
|
||||
if (
|
||||
!(await confirm(
|
||||
ctx,
|
||||
`Are you sure you want to delete **all** ${pairs.length.toLocaleString()} accounts?`
|
||||
))
|
||||
)
|
||||
return;
|
||||
|
||||
for (const [discordID, lbryID] of pairs) {
|
||||
try {
|
||||
await this.lbryx.deleteAccount(discordID, lbryID);
|
||||
} catch (e) {
|
||||
return stripIndents`
|
||||
Failed to delete an account. An error most likely occured while backing up the wallet.
|
||||
\`\`\`\n${e.toString()}\`\`\`
|
||||
`;
|
||||
}
|
||||
}
|
||||
return 'Deleted all accounts.';
|
||||
}
|
||||
}
|
25
src/commands/admin/deposit.ts
Normal file
25
src/commands/admin/deposit.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { DexareClient } from 'dexare';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class DepositCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'deposit',
|
||||
description: 'Gets the address of the master wallet.',
|
||||
category: 'Admin',
|
||||
aliases: ['dp'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['deposit']
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run() {
|
||||
const accountID = await this.lbryx.getDefaultAccount();
|
||||
const addresses = await this.lbry.addressList({ account_id: accountID, page_size: 1 });
|
||||
return `Address: ${addresses.items[0].address}`;
|
||||
}
|
||||
}
|
40
src/commands/admin/fund.ts
Normal file
40
src/commands/admin/fund.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { stripIndents } from 'common-tags';
|
||||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { confirm, ensureDecimal, resolveUser } from '../../util';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class FundCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'fund',
|
||||
description: "Fund a user's account with some LBC.",
|
||||
category: 'Admin',
|
||||
aliases: ['fundacc', 'fundaccount'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['fund @user 2.0'],
|
||||
usage: '<user> <amount>'
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
const discordID = resolveUser(ctx.args[0]);
|
||||
if (!discordID) return "That Discord user isn't valid.";
|
||||
const account = await this.lbryx.ensureAccount(discordID, false);
|
||||
if (!account.id) return 'That user does not have an account.';
|
||||
const amount = ensureDecimal(ctx.args[1]);
|
||||
if (!amount) return 'You must give a numeric amount of LBC to send!';
|
||||
|
||||
if (!(await confirm(ctx, `Are you sure you want to fund this account ${amount} LBC?`))) return;
|
||||
|
||||
const transaction = await this.lbry.accountFund({ amount, to_account: account.id, broadcast: true });
|
||||
this.log('info', `Funded account ${account.id} ${amount}`, transaction);
|
||||
return stripIndents`
|
||||
Successfully funded account!
|
||||
🔗 https://explorer.lbry.com/tx/${transaction.txid}
|
||||
`;
|
||||
}
|
||||
}
|
77
src/commands/admin/fundall.ts
Normal file
77
src/commands/admin/fundall.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { confirm, ensureDecimal, wait } from '../../util';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class FundAllCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'fundall',
|
||||
description: 'Fund all users in the database with some LBC.',
|
||||
category: 'Admin',
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['fundall 2.0'],
|
||||
usage: '<amount>'
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
const amount = ensureDecimal(ctx.args[0]);
|
||||
if (!amount) return 'You must give a numeric amount of LBC to send!';
|
||||
|
||||
await this.lbryx.sync();
|
||||
const pairs = this.lbryx.getIDs();
|
||||
|
||||
// empty DB population
|
||||
if (pairs.length <= 0) {
|
||||
const procMsg = await ctx.reply('No users in the database, creating accounts...');
|
||||
await this.client.startTyping(ctx.channel.id);
|
||||
const rolesConfig: string | string[] = this.client.config.curatorRoles;
|
||||
const curatorRoles = Array.isArray(rolesConfig) ? rolesConfig : [rolesConfig];
|
||||
const members = await this.client.bot.guilds.get(this.client.config.guildID)!.fetchMembers();
|
||||
for (const member of members) {
|
||||
if (curatorRoles.map((r) => member.roles.includes(r)).includes(true)) {
|
||||
const account = await this.lbryx.ensureAccount(member.id);
|
||||
pairs.push([member.id, account.id]);
|
||||
}
|
||||
}
|
||||
await wait(5000);
|
||||
this.client.stopTyping(ctx.channel.id);
|
||||
await procMsg.delete().catch(() => {});
|
||||
}
|
||||
|
||||
if (!(await confirm(ctx, `Are you sure you want to fund **all** accounts ${amount} LBC?`))) return;
|
||||
|
||||
await this.client.startTyping(ctx.channel.id);
|
||||
const resultLines = [];
|
||||
let funded = 0,
|
||||
errored = 0;
|
||||
for (const [discordID, accountID] of pairs) {
|
||||
try {
|
||||
const transaction = await this.lbry.accountFund({ to_account: accountID, amount, broadcast: true });
|
||||
console.info('Funded account', accountID, transaction.txid);
|
||||
resultLines.push(`${discordID} - https://explorer.lbry.com/tx/${transaction.txid}`);
|
||||
funded++;
|
||||
} catch (e) {
|
||||
this.log('info', 'Failed to fund account', accountID, e);
|
||||
resultLines.push(`${discordID} ! ${e.toString()}`);
|
||||
errored++;
|
||||
}
|
||||
await wait(3000);
|
||||
}
|
||||
this.client.stopTyping(ctx.channel.id);
|
||||
|
||||
await ctx.reply(
|
||||
errored
|
||||
? `Failed to fund ${errored} accounts! (${funded} funded)`
|
||||
: `Successfully funded ${funded} account(s)!`,
|
||||
{
|
||||
name: 'result.txt',
|
||||
file: Buffer.from(resultLines.join('\n'), 'utf8')
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
51
src/commands/admin/listall.ts
Normal file
51
src/commands/admin/listall.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { stripIndents } from 'common-tags';
|
||||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { Balance } from '../../modules/lbry/types';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
import { paginate } from '../../util/pager';
|
||||
|
||||
export default class ListAllCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'listall',
|
||||
description: 'List all users in the database.',
|
||||
category: 'Admin',
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['listall', 'listall 2'],
|
||||
usage: '[page]'
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
const pairs = this.lbryx.getIDs();
|
||||
const walletMap = new Map<string, Balance>();
|
||||
|
||||
for (const [, accountID] of pairs) {
|
||||
try {
|
||||
const wallet = await this.lbry.accountBalance({ account_id: accountID });
|
||||
walletMap.set(accountID, wallet);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
await paginate(ctx, {
|
||||
title: 'Users',
|
||||
items: pairs.map(([discordID, accountID]) => {
|
||||
const bal = walletMap.get(accountID);
|
||||
return stripIndents`
|
||||
> <@${discordID}> - \`${accountID}\`
|
||||
> ${
|
||||
bal
|
||||
? `${bal.available} available, ${bal.reserved_subtotals.supports} staked.`
|
||||
: 'Wallet Unavailable'
|
||||
}
|
||||
`;
|
||||
}),
|
||||
itemSeparator: '\n\n',
|
||||
startPage: parseInt(ctx.args[0])
|
||||
});
|
||||
}
|
||||
}
|
23
src/commands/admin/sync.ts
Normal file
23
src/commands/admin/sync.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { DexareClient } from 'dexare';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class SyncCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'sync',
|
||||
description: 'Syncs ID pairs with the SDK.',
|
||||
category: 'Admin',
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['sync']
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run() {
|
||||
const synced = await this.lbryx.sync();
|
||||
return `Synced ${synced.toLocaleString()} new pairs.`;
|
||||
}
|
||||
}
|
53
src/commands/admin/withdraw.ts
Normal file
53
src/commands/admin/withdraw.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { oneLine, stripIndents } from 'common-tags';
|
||||
import { CommandContext, DexareClient } from 'dexare';
|
||||
import { confirm, ensureDecimal } from '../../util';
|
||||
import { GeneralCommand } from '../../util/abstracts';
|
||||
|
||||
export default class WithdrawCommand extends GeneralCommand {
|
||||
constructor(client: DexareClient<any>) {
|
||||
super(client, {
|
||||
name: 'withdraw',
|
||||
description: 'Sends funds to an address from the master wallet.',
|
||||
category: 'Admin',
|
||||
aliases: ['wd'],
|
||||
userPermissions: ['lbry.admin'],
|
||||
metadata: {
|
||||
examples: ['wd abcd1234 2.0']
|
||||
}
|
||||
});
|
||||
|
||||
this.filePath = __filename;
|
||||
}
|
||||
|
||||
async run(ctx: CommandContext) {
|
||||
const addr = ctx.args[0];
|
||||
if (!addr || !/^[\w]{34}$/.test(addr)) return 'The address is invalid!';
|
||||
const amount = ensureDecimal(ctx.args[1]);
|
||||
if (!amount) return 'You must give a numeric amount of LBC to send!';
|
||||
|
||||
// Check if the balance is more than requested
|
||||
const balance = await this.lbry.walletBalance();
|
||||
const availableBalance = parseFloat(balance.available);
|
||||
if (parseFloat(amount) > availableBalance)
|
||||
return 'There is not enough available LBC in the wallet to send that amount!';
|
||||
|
||||
// Send to wallet
|
||||
if (
|
||||
!(await confirm(
|
||||
ctx,
|
||||
oneLine`
|
||||
Are you sure you want to send ${amount} to \`${addr}\`?
|
||||
*(remaining: ${availableBalance - parseFloat(amount)})*
|
||||
`
|
||||
))
|
||||
)
|
||||
return;
|
||||
|
||||
const transaction = await this.lbry.walletSend({ amount, addresses: addr });
|
||||
this.log('info', `Withdrew ${amount} from master wallet to ${addr}`, transaction);
|
||||
return stripIndents`
|
||||
Sent ${amount} LBC to \`${addr}\`.
|
||||
🔗 https://explorer.lbry.com/tx/${transaction.txid}
|
||||
`;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue