Finalize setEthAddress command

This commit is contained in:
Hammad Jutt
2021-05-08 16:12:18 -06:00
committed by Alec LaLonde
parent 0d6f01d3a2
commit a512b0ac76
9 changed files with 139 additions and 101 deletions

View File

@@ -761,46 +761,45 @@ declare module 'sourcecred' {
ledger: Ledger;
persist: () => Promise<ReloadResult>;
}
export interface Ledger {
accounts: () => SCAccountInfo[];
account: (id: string) => SCAccountInfo;
accountByAddress: (address: string) => SCAccountInfo;
addAlias: (identityId: any, alias: any) => void;
activate: (identityId: any) => void;
}
export interface ReloadResult {
error: string | null;
localChanges: any;
}
export interface SCReadInstance {
readCredGraph: () => Promise<CredGraph>;
}
export type CredGraph = {
}
export type CredGraph = Record<string, unknown>;
export interface SCAccountsData {
accounts: SCAccount[];
intervalEndpoints: number[];
unclaimedAliases: SCUnclaimedAlias[];
}
export interface SCAccount {
account: SCAccountInfo;
cred: number[];
totalCred: number;
}
export interface SCAccountInfo {
active: boolean;
balance: string;
identity: SCIdentity;
paid: string;
}
export interface SCIdentity {
address: string;
aliases: SCAlias[];
@@ -808,18 +807,18 @@ declare module 'sourcecred' {
name: string;
subtype: string;
}
export interface SCAlias {
address: string;
description: string;
}
export interface SCUnclaimedAlias {
alias: SCAlias;
cred: number[];
totalCred: number;
}
export interface AddressBookEntry {
name: string;
createdAt: number;

View File

@@ -9,7 +9,7 @@
"start": "node ./dist/start.js",
"build": "yarn generate && tsc -b",
"dev": "concurrently \"yarn dev-ts\" \"yarn generate --watch\"",
"dev-ts": "ts-node-dev --exit-child --respawn -- src/index.ts",
"dev-ts": "ts-node-dev --exit-child --respawn -- src/start.ts",
"typecheck": "yarn build",
"precommit": "yarn lint-staged",
"generate": "graphql-codegen --config=codegen.yml",

View File

@@ -27,10 +27,7 @@ function parseEnv<T extends string | number>(
export const CONFIG: IConfig = {
port: parseEnv(process.env.PORT, 5000),
graphqlURL: (() => {
const {
GRAPHQL_URL: url,
GRAPHQL_HOST: host,
} = process.env;
const { GRAPHQL_URL: url, GRAPHQL_HOST: host } = process.env;
if (url) return url;
if (host) {
@@ -38,7 +35,10 @@ export const CONFIG: IConfig = {
}
return 'http://localhost:8080/v1/graphql';
})(),
adminKey: parseEnv(process.env.HASURA_GRAPHQL_ADMIN_SECRET, 'metagame_secret'),
adminKey: parseEnv(
process.env.HASURA_GRAPHQL_ADMIN_SECRET,
'metagame_secret',
),
frontendUrl: parseEnv(process.env.FRONTEND_URL, 'http://localhost:3000'),
githubApiToken: parseEnv(process.env.GITHUB_API_TOKEN, ''),
discordBotToken: parseEnv(process.env.DISCORD_BOT_TOKEN, ''),

View File

@@ -4,7 +4,8 @@ import * as Path from 'path';
@Discord('', {
import: [
// We are using tsc, so we want to load the compiled files
Path.join(__dirname, "commands", "*.js"),
Path.join(__dirname, 'commands', '*.ts'),
Path.join(__dirname, 'commands', '*.js'),
],
})
export abstract class AppDiscord {

View File

@@ -1,10 +1,15 @@
import { Constants } from "@metafam/utils";
import { Command, CommandMessage } from "@typeit/discord";
import { MessageEmbed, Snowflake } from "discord.js";
import fetch from "node-fetch";
import { SCAccount, SCAccountsData, SCAlias, sourcecred as sc } from "sourcecred";
import { Constants } from '@metafam/utils';
import { Command, CommandMessage } from '@typeit/discord';
import { MessageEmbed, Snowflake } from 'discord.js';
import fetch from 'node-fetch';
import {
SCAccount,
SCAccountsData,
SCAlias,
sourcecred as sc,
} from 'sourcecred';
import { getDiscordId, replyWithUnexpectedError } from "../../utils";
import { getDiscordId, replyWithUnexpectedError } from '../../utils';
export class GetXpCommand {
// todo rename to xp once previous bot is disabled
@@ -14,16 +19,20 @@ export class GetXpCommand {
try {
if (message.args.discordUser) {
targetUserDiscordId = getDiscordId(message.args.discordUser);
} else if (message.member?.id ) {
} else if (message.member?.id) {
targetUserDiscordId = message.member.id;
}
} catch (e) {
await message.reply(`Could not recognize user ${message.args.discordUser}. Try \`!ac help\` if you need help.`);
await message.reply(
`Could not recognize user ${message.args.discordUser}. Try \`!ac help\` if you need help.`,
);
return;
}
if (targetUserDiscordId.trim().length === 0) {
await message.reply(`Could not recognize user. Try \`!ac help\` if you need help.`);
await message.reply(
`Could not recognize user. Try \`!ac help\` if you need help.`,
);
return;
}
@@ -34,60 +43,66 @@ export class GetXpCommand {
await fetch(Constants.SC_ACCOUNTS_FILE)
).json();
const scAccount = accountsData.accounts.find(account => filterAccount(account, targetUserDiscordId));
const scAccount = accountsData.accounts.find((account) =>
filterAccount(account, targetUserDiscordId),
);
if (scAccount != null) {
const userTotalCred = scAccount.totalCred;
const numWeeks = scAccount.cred.length;
const userWeeklyCred = scAccount.cred;
const variation =
(100 * (userWeeklyCred[numWeeks - 1] - userWeeklyCred[numWeeks - 2])) /
(100 *
(userWeeklyCred[numWeeks - 1] - userWeeklyCred[numWeeks - 2])) /
userWeeklyCred[numWeeks - 2];
const description = message.member?.id === targetUserDiscordId ?
`${discordUser}, here is your XP progression in MetaGame` :
`Here is the XP progression of ${discordUser} in MetaGame`;
const description =
message.member?.id === targetUserDiscordId
? `${discordUser}, here is your XP progression in MetaGame`
: `Here is the XP progression of ${discordUser} in MetaGame`;
await message.reply(new MessageEmbed()
.setColor('#ff3864')
.setDescription(description)
.setTitle(`MetaGame XP`)
.setURL("https://xp.metagame.wtf/#/explorer")
.setTimestamp()
.setThumbnail(
'https://raw.githubusercontent.com/sourcecred/sourcecred/master/src/assets/logo/rasterized/logo_64.png',
)
.addFields(
{
name: 'Total',
value: `${Math.round(userTotalCred)} XP`,
inline: true,
},
{
name: 'Last week ',
value: `${userWeeklyCred[numWeeks - 1].toPrecision(3)} XP`,
inline: true,
},
{
name: 'Week before',
value: `${userWeeklyCred[numWeeks - 2].toPrecision(4)} XP`,
inline: true,
},
{
name: 'Weekly Change',
value: `${variation.toPrecision(2)}%`,
inline: true,
},
)
.setFooter(
'Bot made by MetaFam',
'https://wiki.metagame.wtf/img/mg-crystal.png',
),
await message.reply(
new MessageEmbed()
.setColor('#ff3864')
.setDescription(description)
.setTitle(`MetaGame XP`)
.setURL('https://xp.metagame.wtf/#/explorer')
.setTimestamp()
.setThumbnail(
'https://raw.githubusercontent.com/sourcecred/sourcecred/master/src/assets/logo/rasterized/logo_64.png',
)
.addFields(
{
name: 'Total',
value: `${Math.round(userTotalCred)} XP`,
inline: true,
},
{
name: 'Last week ',
value: `${userWeeklyCred[numWeeks - 1].toPrecision(3)} XP`,
inline: true,
},
{
name: 'Week before',
value: `${userWeeklyCred[numWeeks - 2].toPrecision(4)} XP`,
inline: true,
},
{
name: 'Weekly Change',
value: `${variation.toPrecision(2)}%`,
inline: true,
},
)
.setFooter(
'Bot made by MetaFam',
'https://wiki.metagame.wtf/img/mg-crystal.png',
),
);
} else {
await message.reply(`I couldn't find ${discordUser} in the ledger! Have you registered yet?`)
await message.reply(
`I couldn't find ${discordUser} in the ledger! Have you registered yet?`,
);
}
}
catch (e) {
} catch (e) {
await replyWithUnexpectedError(message);
}
}
@@ -98,27 +113,32 @@ const filterAccount = (player: SCAccount, targetUserDiscordID: Snowflake) => {
// Ignore if the target isn't a USER
if (accountInfo.identity.subtype !== 'USER') return false;
const discordAliases = accountInfo.identity.aliases.filter(
alias => {
const parts = sc.core.graph.NodeAddress.toParts(alias.address);
return parts.indexOf('discord') > 0
}
);
const discordAliases = accountInfo.identity.aliases.filter((alias) => {
const parts = sc.core.graph.NodeAddress.toParts(alias.address);
return parts.indexOf('discord') > 0;
});
if (discordAliases.length >= 1) {
// Retrieve the Discord ID
const discordId = discordAliases.find(discordAccount => scAliasMatchesDiscordId(discordAccount, targetUserDiscordID));
if (discordId !== undefined){
const discordId = discordAliases.find((discordAccount) =>
scAliasMatchesDiscordId(discordAccount, targetUserDiscordID),
);
if (discordId !== undefined) {
return accountInfo;
}
}
return false;
}
};
const scAliasMatchesDiscordId = (discordAccount: SCAlias, targetUserDiscordID: Snowflake) => {
const discordId = sc.core.graph.NodeAddress.toParts(discordAccount.address)[4];
const scAliasMatchesDiscordId = (
discordAccount: SCAlias,
targetUserDiscordID: Snowflake,
) => {
const discordId = sc.core.graph.NodeAddress.toParts(
discordAccount.address,
)[4];
if (discordId === targetUserDiscordID) {
return discordId;
}
return undefined;
}
};

View File

@@ -1,12 +1,12 @@
import { CommandMessage } from "@typeit/discord";
import { ReloadResult, sourcecred as sc } from "sourcecred";
import { Command, CommandMessage } from '@typeit/discord';
import { sourcecred as sc } from 'sourcecred';
import { loadSourceCredLedger, manager } from "../../sourcecred";
import { loadSourceCredLedger, manager } from '../../sourcecred';
const addressUtils = sc.plugins.ethereum.utils.address;
export abstract class SetEthAddress {
// @Command('setAddress :ethAddress')
@Command('!setAddress :ethAddress')
async setAddress(message: CommandMessage) {
const res = await loadSourceCredLedger();
@@ -23,7 +23,7 @@ export abstract class SetEthAddress {
baseIdentityProposal,
);
let ethAddress;
let ethAddress: string;
try {
ethAddress = addressUtils.parseAddress(message.args.ethAddress);
} catch (e) {
@@ -45,10 +45,24 @@ export abstract class SetEthAddress {
return;
}
const account = manager.ledger.account(baseIdentityId);
const existing = account.identity.aliases.find((alias) => {
const parts = sc.core.graph.NodeAddress.toParts(alias.address);
return parts.indexOf('ethereum') > 0;
});
if (existing) {
await message.reply(
`You already have linked the following ETH Address: \`${existing.description}\`.`,
);
return;
}
try {
manager.ledger.addAlias(baseIdentityId, ethAlias);
manager.ledger.activate(baseIdentityId);
const persistRes: ReloadResult = await manager.persist();
const persistRes = await manager.persist();
if (persistRes.error) {
await message.reply(

View File

@@ -7,6 +7,7 @@ async function createDiscordClient(): Promise<Client> {
const client = new Client({
classes: [
// We are using tsc, so we want to load the compiled files
`${__dirname}/discord/**/*.ts`, // glob string to load the classes
`${__dirname}/discord/**/*.js`, // glob string to load the classes
],
silent: false,
@@ -14,7 +15,6 @@ async function createDiscordClient(): Promise<Client> {
});
await client.login(CONFIG.discordBotToken);
return client;
}

View File

@@ -1,4 +1,4 @@
import { LedgerManager, ReloadResult, sourcecred } from "sourcecred";
import { LedgerManager, ReloadResult, sourcecred } from 'sourcecred';
import { CONFIG } from './config';
@@ -8,9 +8,11 @@ const storage = new sourcecred.ledger.storage.GithubStorage({
branch: 'master',
});
export const manager: LedgerManager = new sourcecred.ledger.manager.LedgerManager({
storage,
});
export const manager: LedgerManager = new sourcecred.ledger.manager.LedgerManager(
{
storage,
},
);
let loading = false;
let ledgerLoadedPromise: Promise<ReloadResult>;
@@ -19,7 +21,7 @@ export const loadSourceCredLedger = (): Promise<ReloadResult> => {
if (ledgerLoadedPromise == null) {
if (!loading) {
loading = true;
console.log('reloading ledger...')
console.log('reloading ledger...');
ledgerLoadedPromise = manager.reloadLedger();
ledgerLoadedPromise.then(() => {
loading = false;
@@ -28,4 +30,4 @@ export const loadSourceCredLedger = (): Promise<ReloadResult> => {
}
return ledgerLoadedPromise;
}
};

View File

@@ -1,9 +1,9 @@
import express from 'express';
import { createDiscordClient } from ".";
import { createDiscordClient } from '.';
import { CONFIG } from './config';
createDiscordClient();
const discordClientPromise = createDiscordClient();
const app = express();
@@ -12,5 +12,7 @@ app.get('/healthz', (_, res) => {
});
app.listen(CONFIG.port, () => {
console.log(`Discord bot started on port ${CONFIG.port}`);
discordClientPromise.then(() => {
console.log(`Discord bot started on port ${CONFIG.port}`);
});
});