From 6052ed37f1d8d9d2de94ee4cffa83bb9db0ea75c Mon Sep 17 00:00:00 2001 From: Juan Girini Date: Fri, 18 Oct 2024 13:55:07 +0200 Subject: [PATCH 1/2] chore: styling --- .github/workflows/lint.yml | 28 +++++ .husky/pre-commit | 1 + src/discord.js | 204 ++++++++++++++++++++----------------- 3 files changed, 138 insertions(+), 95 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100755 .husky/pre-commit diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..e208b0b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,28 @@ +name: Lint Check + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Run Prettier check + run: npx prettier --check . diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/src/discord.js b/src/discord.js index af0a363..915a2d0 100644 --- a/src/discord.js +++ b/src/discord.js @@ -16,19 +16,24 @@ import { Client, GatewayIntentBits, Partials } from 'discord.js' import { BN } from 'bn.js' -import { MurmurService, generateSecret, isAuthenticated, executeWithRetries } from './index.js' +import { + MurmurService, + generateSecret, + isAuthenticated, + executeWithRetries, +} from './index.js' import dotenv from 'dotenv' dotenv.config({ path: '.env.discord' }) const DISCORD = 'discord' const client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.DirectMessages, - ], - partials: [Partials.Channel] + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.DirectMessages, + ], + partials: [Partials.Channel], }) // Store user sessions to track authentication const userSessions = {} @@ -36,105 +41,115 @@ const userSessions = {} // Instantiate the service const murmurService = new MurmurService() // Discord bot commands -client.on('messageCreate', async message => { - if (message.author.bot) return +client.on('messageCreate', async (message) => { + if (message.author.bot) return - const args = message.content.split(' ') + const args = message.content.split(' ') - console.log(args) - let command = args[1] + console.log(args) + let command = args[1] - if (command === undefined) return + if (command === undefined) return - command = command.toLowerCase() - console.log(command) + command = command.toLowerCase() + console.log(command) - if (command === '!auth') { - const secret = generateSecret(message.author.id) - const username = message.author.username + DISCORD - console.log('calling authenticate') + if (command === '!auth') { + const secret = generateSecret(message.author.id) + const username = message.author.username + DISCORD + console.log('calling authenticate') - try { - await murmurService.authenticate(username, secret) - userSessions[message.author.id] = { authenticated: true, secret } - message.reply('Authenticated successfully.') - } catch (err) { - console.log(err) - message.reply('Authentication failed.') - } + try { + await murmurService.authenticate(username, secret) + userSessions[message.author.id] = { authenticated: true, secret } + message.reply('Authenticated successfully.') + } catch (err) { + console.log(err) + message.reply('Authentication failed.') } + } - if (command === '!drip') { - if (!isAuthenticated(message.author.id, userSessions)) { - return message.reply('Please authenticate using `!auth` first.') - } - - // ensure a proxy exists first - const username = message.author.username - const userInfo = await murmurService.inspectUser(username + DISCORD) - if (userInfo.address == '') return message.reply('You must call !create first') - const address = userInfo.address; - murmurService.faucet(address, () => { - message.reply(`Sent 500 tokens to @${username}.`) - }); + if (command === '!drip') { + if (!isAuthenticated(message.author.id, userSessions)) { + return message.reply('Please authenticate using `!auth` first.') } - // Create command (requires authentication) - if (command === '!create') { - if (!isAuthenticated(message.author.id, userSessions)) { - return message.reply('Please authenticate using `!auth` first.') - } - - const validity = parseInt(args[2]) || 10 // Set default validity - murmurService.createNew(validity, (result) => { - if (result.status.isInBlock) - message.reply(`Created new entry with validity: ${validity}.`) - }) + // ensure a proxy exists first + const username = message.author.username + const userInfo = await murmurService.inspectUser(username + DISCORD) + if (userInfo.address == '') + return message.reply('You must call !create first') + const address = userInfo.address + murmurService.faucet(address, () => { + message.reply(`Sent 500 tokens to @${username}.`) + }) + } + + // Create command (requires authentication) + if (command === '!create') { + if (!isAuthenticated(message.author.id, userSessions)) { + return message.reply('Please authenticate using `!auth` first.') } - // Execute command (requires authentication) - if (command === '!execute') { - if (!isAuthenticated(message.author.id, userSessions)) { - return message.reply('Please authenticate using `!auth` first.') - } - - const username = message.author.username - const userInfo = await murmurService.inspectUser(username + DISCORD) - if (userInfo === '') - return message.reply('You must first create a wallet with !create') - - // todo: get recipient - const recipient = args[2] - // todo: get amount - const amount = args[3] - - let balance = new BN(amount * Math.pow(10, 12)) - let tx = await murmurService.api - .tx - .balances - .transferKeepAlive(recipient, balance) - - - executeWithRetries(murmurService, tx, - () => { - message.reply(`@${username}, transaction executed successfully.`); - }, (retries) => { - message.reply(`@${username}, transaction execution failed (timing). Attempting retry ${retries + 1} of ${process.env.MAX_RETRIES}`) - }, () => { - message.reply(`@${username}, maximum retry limit reached. Transaction failed.`) - }) - - } - - // Inspect command (publicly accessible) - if (command === '!inspect') { - const username = message.author.username - const userInfo = await murmurService.inspectUser(username + DISCORD) - message.reply(`User ${username} has address: ${userInfo.address} and balance: ${userInfo.balance}`) + const validity = parseInt(args[2]) || 10 // Set default validity + murmurService.createNew(validity, (result) => { + if (result.status.isInBlock) + message.reply(`Created new entry with validity: ${validity}.`) + }) + } + + // Execute command (requires authentication) + if (command === '!execute') { + if (!isAuthenticated(message.author.id, userSessions)) { + return message.reply('Please authenticate using `!auth` first.') } - if (command === '!help') { - message.reply(` + const username = message.author.username + const userInfo = await murmurService.inspectUser(username + DISCORD) + if (userInfo === '') + return message.reply('You must first create a wallet with !create') + + // todo: get recipient + const recipient = args[2] + // todo: get amount + const amount = args[3] + + let balance = new BN(amount * Math.pow(10, 12)) + let tx = await murmurService.api.tx.balances.transferKeepAlive( + recipient, + balance + ) + + executeWithRetries( + murmurService, + tx, + () => { + message.reply(`@${username}, transaction executed successfully.`) + }, + (retries) => { + message.reply( + `@${username}, transaction execution failed (timing). Attempting retry ${retries + 1} of ${process.env.MAX_RETRIES}` + ) + }, + () => { + message.reply( + `@${username}, maximum retry limit reached. Transaction failed.` + ) + } + ) + } + + // Inspect command (publicly accessible) + if (command === '!inspect') { + const username = message.author.username + const userInfo = await murmurService.inspectUser(username + DISCORD) + message.reply( + `User ${username} has address: ${userInfo.address} and balance: ${userInfo.balance}` + ) + } + + if (command === '!help') { + message.reply(` Available Commands: !auth - Authenticate with the service. !create - Create a new entry with optional validity. @@ -143,12 +158,11 @@ client.on('messageCreate', async message => { !inspect - Inspect your current balance and address. !help - Display the help message `) - } + } }) client.once('ready', () => { - console.log('Bot is online!') + console.log('Bot is online!') }) client.login(process.env.DISCORD_BOT_TOKEN) - From a68ddafc5755dd8677da4bdb94911f10278101be Mon Sep 17 00:00:00 2001 From: Juan Girini Date: Fri, 18 Oct 2024 13:57:04 +0200 Subject: [PATCH 2/2] chore: license and styling --- .prettierrc | 2 +- README.md | 31 +++++-- nodemon.json | 12 ++- package-lock.json | 18 +++- package.json | 11 ++- src/index.js | 206 +++++++++++++++++++++++-------------------- src/twitch.js | 216 +++++++++++++++++++++++++++------------------- tsconfig.json | 13 --- 8 files changed, 291 insertions(+), 218 deletions(-) delete mode 100644 tsconfig.json diff --git a/.prettierrc b/.prettierrc index 5bb3a1f..009151d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -9,4 +9,4 @@ "quoteProps": "consistent", "bracketSpacing": true, "endOfLine": "auto" -} \ No newline at end of file +} diff --git a/README.md b/README.md index 886ea62..6dab413 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Murmur Bots -This is a collection of node.js-based implementations of the murmur protocol. Specifically, this repo contains bot integrations for both [Discord](https://discordapp.com/) and [Twitch](https://www.twitch.tv/), enabling per-channel/server wallets via Murmur. +This is a collection of node.js-based implementations of the [Murmur protocol](https://github.com/ideal-lab5/murmur). Specifically, this repo contains bot integrations for both [Discord](https://discordapp.com/) and [Twitch](https://www.twitch.tv/), enabling per-channel/server wallets via Murmur. To learn more about the Murmur protocol, visit the documentation at https://murmur.idealabs.network. @@ -10,23 +10,24 @@ To learn more about the Murmur protocol, visit the documentation at https://murm #### Setup -First create a file called .env.discord in the root of this repo containing the following keys: +First create a file called `.env.discord` in the root of this repo containing the following keys: -``` +``` MAX_RETRIES=number of times to retry failed transactions SECRET_SALT=any string DISCORD_BOT_TOKEN=a valid discord bot token for the server ``` -Once configured, install dependencies and run the bot with -``` shell +Once configured, install dependencies and run the Discord bot with + +```shell npm i npm run discord ``` ### Twitch -Create a file called .env.twitch in the root of this repo containing the following keys: +Create a file called `.env.twitch` in the root of this repo containing the following keys: ``` MAX_RETRIES= @@ -36,9 +37,21 @@ TWITCH_OAUTH_TOKEN= TWITCH_CHANNEL= ``` -Then instsall depenencies and run the twitch bot with +Then instsall depenencies and run the Twitch bot with -``` shell +```shell npm i npm run twitch -``` \ No newline at end of file +``` + +## Contributing + +Contributions are welcome! Please open an issue or submit a pull request. + +## License + +This project is licensed under the Apache-2.0. See the [LICENSE](LICENSE) file for details. + +## Contact + +For any inquiries, please contact [Ideal Labs](https://idealabs.network). diff --git a/nodemon.json b/nodemon.json index 4669018..c498d7d 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,8 +1,6 @@ { - "watch": [ - "src" - ], - "ext": ".js", - "ignore": [], - "exec": "node dist/server.js" -} \ No newline at end of file + "watch": ["src"], + "ext": ".js", + "ignore": [], + "exec": "node dist/server.js" +} diff --git a/package-lock.json b/package-lock.json index 0b32b91..73a1658 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "murmur-bots", "version": "1.0.0", - "license": "ISC", + "license": "Apache-2.0", "dependencies": { "@ideallabs/murmur.js": "^0.1.0", "@polkadot/api": "^14.0.1", @@ -21,6 +21,7 @@ }, "devDependencies": { "@tsconfig/node20": "^20.1.4", + "husky": "^8.0.0", "lint-staged": "^15.2.10", "prettier": "^3.3.3", "prettier-plugin-organize-imports": "^4.1.0" @@ -1482,6 +1483,21 @@ "node": ">=16.17.0" } }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/is-fullwidth-code-point": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", diff --git a/package.json b/package.json index aef459f..59e2e1a 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,15 @@ "version": "1.0.0", "description": "Utilities for using Murmur in nodejs", "main": "index.js", + "repository": "https://github.com/ideal-lab5/murmur-bots", "scripts": { "discord": "node src/discord.js", "twitch": "node src/twitch.js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "prepare": "husky install" }, - "author": "", - "license": "ISC", + "author": "Ideal Labs ", + "license": "Apache-2.0", "dependencies": { "@ideallabs/murmur.js": "^0.1.0", "@polkadot/api": "^14.0.1", @@ -26,7 +28,8 @@ "@tsconfig/node20": "^20.1.4", "lint-staged": "^15.2.10", "prettier": "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0" + "prettier-plugin-organize-imports": "^4.1.0", + "husky": "^8.0.0" }, "lint-staged": { "*.{js,jsx,ts,tsx,json,md}": [ diff --git a/src/index.js b/src/index.js index 7afad66..ee56df7 100644 --- a/src/index.js +++ b/src/index.js @@ -31,110 +31,126 @@ const BAD_CT_ERROR = '0x00000000' const BAD_MT_ERROR = '0x03000000' export class MurmurService { - constructor() { - this.api = null - this.client = null - this.wsUrl = FALLBACK_NODE_WS - this.apiUrl = FALLBACK_API_URL - - this.init(this.wsUrl).then(() => { - console.log('MurmurService ready.') - }) - } - - async init(providerMultiAddr) { - this.api = await this.setupPolkadotJs(providerMultiAddr) - - const httpClient = axios.create({ - baseURL: this.apiUrl, - headers: { - 'Content-Type': 'application/json', - }, - }) - - const keyring = new Keyring({ type: 'sr25519' }) - const alice = keyring.addFromUri('//Alice') - this.client = new MurmurClient(httpClient, this.api, alice) - - console.log('MurmurClient initialized') - } - - async setupPolkadotJs(providerMultiAddr) { - const provider = new WsProvider(providerMultiAddr) - return await ApiPromise.create({ provider }) - } - - async authenticate(username, password) { - return this.client.authenticate(username, password) - } - - async createNew(validity, callback) { - return this.client.new(validity, callback) - } - - async executeTransaction(extrinsic, callback) { - return this.client.execute(extrinsic, callback) - } - - async faucet(address, callback) { - const call = this.api.tx.balances.transferAllowDeath(address, new BN(500 * Math.pow(10, 12))) - const keyring = new Keyring({ type: 'sr25519' }) - const alice = keyring.addFromUri('//Alice') - const unsub = await call.signAndSend(alice, (result) => { - if (result.status.isInBlock) { - callback() - } else if (result.status.isFinalized) { - unsub() - } - }) - } - - async inspectUser(username) { - const result = await this.api.query.murmur.registry(username) - const humanResult = result.toHuman() - if (!humanResult || !humanResult.address) return { address: '', balance: '' } - - const accountData = await this.api.query.system.account(humanResult.address) - const balance = accountData.data.free.toString() - const formattedBalance = new BN(balance / Math.pow(10, 12)) - return { address: humanResult.address, balance: formattedBalance } - } + constructor() { + this.api = null + this.client = null + this.wsUrl = FALLBACK_NODE_WS + this.apiUrl = FALLBACK_API_URL + + this.init(this.wsUrl).then(() => { + console.log('MurmurService ready.') + }) + } + + async init(providerMultiAddr) { + this.api = await this.setupPolkadotJs(providerMultiAddr) + + const httpClient = axios.create({ + baseURL: this.apiUrl, + headers: { + 'Content-Type': 'application/json', + }, + }) + + const keyring = new Keyring({ type: 'sr25519' }) + const alice = keyring.addFromUri('//Alice') + this.client = new MurmurClient(httpClient, this.api, alice) + + console.log('MurmurClient initialized') + } + + async setupPolkadotJs(providerMultiAddr) { + const provider = new WsProvider(providerMultiAddr) + return await ApiPromise.create({ provider }) + } + + async authenticate(username, password) { + return this.client.authenticate(username, password) + } + + async createNew(validity, callback) { + return this.client.new(validity, callback) + } + + async executeTransaction(extrinsic, callback) { + return this.client.execute(extrinsic, callback) + } + + async faucet(address, callback) { + const call = this.api.tx.balances.transferAllowDeath( + address, + new BN(500 * Math.pow(10, 12)) + ) + const keyring = new Keyring({ type: 'sr25519' }) + const alice = keyring.addFromUri('//Alice') + const unsub = await call.signAndSend(alice, (result) => { + if (result.status.isInBlock) { + callback() + } else if (result.status.isFinalized) { + unsub() + } + }) + } + + async inspectUser(username) { + const result = await this.api.query.murmur.registry(username) + const humanResult = result.toHuman() + if (!humanResult || !humanResult.address) + return { address: '', balance: '' } + + const accountData = await this.api.query.system.account(humanResult.address) + const balance = accountData.data.free.toString() + const formattedBalance = new BN(balance / Math.pow(10, 12)) + return { address: humanResult.address, balance: formattedBalance } + } } // Helper function to generate a secret password per user export function generateSecret(userId) { - return crypto.createHash('sha256').update(userId + process.env.SECRET_SALT).digest('hex') + return crypto + .createHash('sha256') + .update(userId + process.env.SECRET_SALT) + .digest('hex') } // Helper to check if a user is authenticated export function isAuthenticated(userId, userSessions) { - return userSessions[userId] && userSessions[userId].authenticated + return userSessions[userId] && userSessions[userId].authenticated } -export async function executeWithRetries(murmurService, tx, successCallback, retryCallback, exhaustedCallback, retries = 0) { - await murmurService.executeTransaction(tx, (result) => { - if (result.dispatchError && - (result.dispatchError.toHuman().Module.error === BAD_CT_ERROR || - result.dispatchError.toHuman().Module.error === BAD_MT_ERROR)) { - console.log('Dispatch error detected'); - setTimeout(async () => { - if (retries < MAX_RETRIES) { - retryCallback(retries) - await executeWithRetries( - murmurService, - tx, - successCallback, - retryCallback, - exhaustedCallback, - retries + 1 - ) - } else { - exhaustedCallback(retries) - return - } - }, 3000) // 3s cooldown (1 block) - } else if (result.status.isInBlock) { - successCallback(result.toHuman()) +export async function executeWithRetries( + murmurService, + tx, + successCallback, + retryCallback, + exhaustedCallback, + retries = 0 +) { + await murmurService.executeTransaction(tx, (result) => { + if ( + result.dispatchError && + (result.dispatchError.toHuman().Module.error === BAD_CT_ERROR || + result.dispatchError.toHuman().Module.error === BAD_MT_ERROR) + ) { + console.log('Dispatch error detected') + setTimeout(async () => { + if (retries < MAX_RETRIES) { + retryCallback(retries) + await executeWithRetries( + murmurService, + tx, + successCallback, + retryCallback, + exhaustedCallback, + retries + 1 + ) + } else { + exhaustedCallback(retries) + return } - }); + }, 3000) // 3s cooldown (1 block) + } else if (result.status.isInBlock) { + successCallback(result.toHuman()) + } + }) } diff --git a/src/twitch.js b/src/twitch.js index ef9b10c..9fa38bb 100644 --- a/src/twitch.js +++ b/src/twitch.js @@ -17,18 +17,23 @@ import tmi from 'tmi.js' import { BN } from 'bn.js' import dotenv from 'dotenv' -import { MurmurService, generateSecret, isAuthenticated, executeWithRetries } from './index.js' +import { + MurmurService, + generateSecret, + isAuthenticated, + executeWithRetries, +} from './index.js' dotenv.config({ path: '.env.twitch' }) const TWITCH = 'twitch' // Twitch bot configuration const client = new tmi.Client({ - identity: { - username: process.env.TWITCH_BOT_USERNAME, - password: process.env.TWITCH_OAUTH_TOKEN, - }, - channels: [process.env.TWITCH_CHANNEL] + identity: { + username: process.env.TWITCH_BOT_USERNAME, + password: process.env.TWITCH_OAUTH_TOKEN, + }, + channels: [process.env.TWITCH_CHANNEL], }) // Store user sessions to track authentication @@ -37,99 +42,133 @@ const userSessions = {} const murmurService = new MurmurService() client.connect().then(() => { - console.log('Twitch Bot is online!') + console.log('Twitch Bot is online!') }) // Twitch bot commands client.on('message', async (channel, tags, message, self) => { - if (self) return // Ignore messages from the bot - - const args = message.split(' ') - let command = args[1] - if (command === undefined) return - command = command.toLowerCase() - - // Auth command - if (command === '!auth') { - const secret = generateSecret(tags['user-id']) - const username = tags['display-name'] - - try { - murmurService.authenticate(username + TWITCH, secret).then(() => { - userSessions[tags['user-id']] = { authenticated: true, secret } - client.say(channel, `@${username}, authenticated successfully.`) - }) - } catch (err) { - console.log(err) - client.say(channel, `@${username}, authentication failed.`) - } + if (self) return // Ignore messages from the bot + + const args = message.split(' ') + let command = args[1] + if (command === undefined) return + command = command.toLowerCase() + + // Auth command + if (command === '!auth') { + const secret = generateSecret(tags['user-id']) + const username = tags['display-name'] + + try { + murmurService.authenticate(username + TWITCH, secret).then(() => { + userSessions[tags['user-id']] = { authenticated: true, secret } + client.say(channel, `@${username}, authenticated successfully.`) + }) + } catch (err) { + console.log(err) + client.say(channel, `@${username}, authentication failed.`) } - - if (command === '!drip') { - if (!isAuthenticated(tags['user-id'], userSessions)) { - return client.say(channel, `@${tags['display-name']}, please authenticate using \`!auth\` first.`) - } - - const username = tags['display-name'] - const userInfo = await murmurService.inspectUser(username + TWITCH) - if (userInfo.address == '') return client.say(channel, 'You must call !create first.') - const address = userInfo.address; - murmurService.faucet(address, () => { - client.say(channel, `Sent 500 tokens to @${username}.`) - }); - + } + + if (command === '!drip') { + if (!isAuthenticated(tags['user-id'], userSessions)) { + return client.say( + channel, + `@${tags['display-name']}, please authenticate using \`!auth\` first.` + ) } - // Create command (requires authentication) - if (command === '!create') { - if (!isAuthenticated(tags['user-id'], userSessions)) { - return client.say(channel, `@${tags['display-name']}, please authenticate using \`!auth\` first.`) - } - - const validity = parseInt(args[2]) || 10 // Set default validity - murmurService.createNew(validity, (result) => { - if (result.status.isInBlock) - client.say(channel, `@${tags['display-name']}, created new entry with validity: ${validity}.`) - }) + const username = tags['display-name'] + const userInfo = await murmurService.inspectUser(username + TWITCH) + if (userInfo.address == '') + return client.say(channel, 'You must call !create first.') + const address = userInfo.address + murmurService.faucet(address, () => { + client.say(channel, `Sent 500 tokens to @${username}.`) + }) + } + + // Create command (requires authentication) + if (command === '!create') { + if (!isAuthenticated(tags['user-id'], userSessions)) { + return client.say( + channel, + `@${tags['display-name']}, please authenticate using \`!auth\` first.` + ) } - // Execute command (requires authentication) - if (command === '!execute') { - if (!isAuthenticated(tags['user-id'], userSessions)) { - return client.say(channel, `@${tags['display-name']}, please authenticate using \`!auth\` first.`) - } - - const username = tags['display-name'] - const userInfo = await murmurService.inspectUser(username + TWITCH) - if (userInfo === '') { - return client.say(channel, `@${username}, you must first create a wallet with !create.`) - } - - const recipient = args[2] - const amount = args[3] - - let balance = new BN(amount * Math.pow(10, 12)) - let tx = await murmurService.api.tx.balances.transferKeepAlive(recipient, balance) - - executeWithRetries(murmurService, tx, - () => { - client.say(channel, `@${username}, transaction executed successfully.`); - }, (retries) => { - client.say(channel, `@${username}, transaction execution failed (timing). Attempting retry ${retries + 1} of ${process.env.MAX_RETRIES}`) - }, () => { - client.say(channel, `@${username}, maximum retry limit reached. Transaction failed.`) - }) + const validity = parseInt(args[2]) || 10 // Set default validity + murmurService.createNew(validity, (result) => { + if (result.status.isInBlock) + client.say( + channel, + `@${tags['display-name']}, created new entry with validity: ${validity}.` + ) + }) + } + + // Execute command (requires authentication) + if (command === '!execute') { + if (!isAuthenticated(tags['user-id'], userSessions)) { + return client.say( + channel, + `@${tags['display-name']}, please authenticate using \`!auth\` first.` + ) } - // Inspect command (publicly accessible) - if (command === '!inspect') { - const username = tags['display-name'] - const userInfo = await murmurService.inspectUser(username + TWITCH) - client.say(channel, `User ${username} has address: ${userInfo.address} and balance: ${userInfo.balance}`) + const username = tags['display-name'] + const userInfo = await murmurService.inspectUser(username + TWITCH) + if (userInfo === '') { + return client.say( + channel, + `@${username}, you must first create a wallet with !create.` + ) } - if (command === '!help') { - client.say(channel, ` + const recipient = args[2] + const amount = args[3] + + let balance = new BN(amount * Math.pow(10, 12)) + let tx = await murmurService.api.tx.balances.transferKeepAlive( + recipient, + balance + ) + + executeWithRetries( + murmurService, + tx, + () => { + client.say(channel, `@${username}, transaction executed successfully.`) + }, + (retries) => { + client.say( + channel, + `@${username}, transaction execution failed (timing). Attempting retry ${retries + 1} of ${process.env.MAX_RETRIES}` + ) + }, + () => { + client.say( + channel, + `@${username}, maximum retry limit reached. Transaction failed.` + ) + } + ) + } + + // Inspect command (publicly accessible) + if (command === '!inspect') { + const username = tags['display-name'] + const userInfo = await murmurService.inspectUser(username + TWITCH) + client.say( + channel, + `User ${username} has address: ${userInfo.address} and balance: ${userInfo.balance}` + ) + } + + if (command === '!help') { + client.say( + channel, + ` Available Commands: !auth - Authenticate with the service. !create - Create a new entry with optional validity. @@ -137,6 +176,7 @@ client.on('message', async (channel, tags, message, self) => { !execute - Execute a token transfer (requires authentication). !inspect - Inspect your current balance and address. !help - Display the help message - `) - } + ` + ) + } }) diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index f695ad5..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@tsconfig/node20/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "outDir": "dist" - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file