From bef28e3e02cdc4afaeaa952a5443ced1f02155c4 Mon Sep 17 00:00:00 2001 From: ProfessorDey Date: Thu, 1 Feb 2018 10:33:18 +0000 Subject: [PATCH 1/8] Optimise Help Msg & Wrong Channel Warning This one involves a fair amount of reworking of the way functions operate, all checks for being in private or bot sandbox have been removed from functions and that functionality is now applied through privateOrSandboxOnly(), passing the message, channel warning, function to be called and then an array of arguments, sans message, as that's automatically attached at the head as all primary functions require the message anyway. while this makes each case entry a bit longer, it does reduce code reuse and potential for old code being left in, since each function had to previously be updated individually if something happened with the way these tests were carried out. I've yet to find a way to allow us to set the export description and the help message in one fell swoop, so for now I've manually set the description. The help message itself is now broken down into pairs of subcommand and description, the subcommand will be indented once, while the description will be indented twice. The way the help function now works is to allow everything to be set at the start, without needing to hunt all the way down to find the duplicate help message. Now everything can be replaced in the main function, though this will require a bit of computation with each request presently, as I don't know if or how the exported functions can access constants defined outside them. This can be optimised later if need be and at the very least shouldn't slow things down very much with the few entries that there are. Unfortunately changing the way that the help message works does involve parameterising the help message itself for any function that requires it, but this also provides a helpful indicator of what functions might fail and fall back on the help function. --- bot/modules/tipbot.js | 67 ++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index e23f67f..440e457 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -10,21 +10,42 @@ exports.commands = [ ] exports.tip = { usage: "", - description: 'balance: get your balance\n deposit: get address for your deposits\n withdraw ADDRESS AMOUNT: withdraw AMOUNT credits to ADDRESS\n [private] : mention a user with @ and then the amount to tip them, or put private before the user to tip them privately.\n Key: [] : Optionally include contained keyword, <> : Replace with appropriate value.', + description: '\t[help]\n\t\tGet this message\n\tbalance\n\t\tGet your balance\n\tdeposit\n\t\tGet address for your deposits\n\twithdraw ADDRESS AMOUNT\n\t\tWithdraw AMOUNT credits to ADDRESS\n\t[private] \n\t\tMention a user with @ and then the amount to tip them, or put private before the user to tip them privately.\nKey: [] : Optionally include contained keyword, <> : Replace with appropriate value.', process: async function (bot, msg, suffix) { let tipper = msg.author.id.replace('!', ''), words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), - subcommand = words.length >= 2 ? words[1] : 'help'; + subcommand = words.length >= 2 ? words[1] : 'help', + helpmsgparts = [['[help]', 'Get this message'], + ['balance', 'Get your balance'], + ['deposit', 'Get address for your deposits'], + ['withdraw ADDRESS AMOUNT', 'Withdraw AMOUNT credits to ADDRESS'], + ['[private] ', 'Mention a user with @ and then the amount to tip them, or put private before the user to tip them privately.']], + helpmsg = '```**!tip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with appropriate value.```', + channelwarning = 'Please use <#369896313082478594> or DMs to talk to bots.'; switch (subcommand) { - case 'help': doHelp(msg); break; + case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; case 'balance': doBalance(msg, tipper); break; - case 'deposit': doDeposit(msg, tipper); break; - case 'withdraw': doWithdraw(msg, tipper, words); break; - default: doTip(msg, tipper, words); + case 'deposit': privateOrSandboxOnly(msg, channelwarning, doDeposit, [tipper]); break; + case 'withdraw': privateOrSandboxOnly(msg, channelwarning, doWithdraw, [tipper, words, helpmsg]); break; + default: doTip(msg, tipper, words, helpmsg); } } } +function privateOrSandboxOnly(message, wrongchannelmsg, fn, args) { + if (!inPrivateOrBotSandbox(message)) { + message.reply(wrongchannelmsg); + return; + } + fn.apply(null, [message, ...args]); +} + + +function doHelp(message, helpmsg) { + message.author.send(helpmsg); +} + + function doBalance(message, tipper) { lbry.getBalance(tipper, 1, function (err, balance) { if (err) { @@ -38,10 +59,6 @@ function doBalance(message, tipper) { function doDeposit(message, tipper) { - if (!inPrivateOrBotSandbox(message)) { - message.reply('Please use <#369896313082478594> or DMs to talk to bots.'); - return; - } getAddress(tipper, function (err, address) { if (err) { message.reply('Error getting deposit address'); @@ -53,18 +70,14 @@ function doDeposit(message, tipper) { } -function doWithdraw(message, tipper, words) { - if (!inPrivateOrBotSandbox(message)) { - message.reply('Please use <#369896313082478594> or DMs to talk to bots.'); - return; - } +function doWithdraw(message, tipper, words, helpmsg) { if (words.length < 4) { - doHelp(message); + doHelp(message, helpmsg); return; } var address = words[2], - amount = getValidatedAmount(words[3]); + amount = getValidatedAmount(words[3]); if (amount === null) { message.reply('I dont know how to withdraw that many credits'); @@ -82,9 +95,9 @@ function doWithdraw(message, tipper, words) { } -function doTip(message, tipper, words) { +function doTip(message, tipper, words, helpmsg) { if (words.length < 3 || !words) { - doHelp(message); + doHelp(message, helpmsg); return; } @@ -111,14 +124,6 @@ function doTip(message, tipper, words) { } -function doHelp(message) { - if (!inPrivateOrBotSandbox(message)) { - message.reply('Sent you help via DM! Please use <#369896313082478594> or DMs to talk to bots.'); - } - message.author.send('**!tip**\n balance: get your balance\n deposit: get address for your deposits\n withdraw ADDRESS AMOUNT: withdraw AMOUNT credits to ADDRESS\n [private] : mention a user with @ and then the amount to tip them, or put private before the user to tip them privately.\n Key: [] : Optionally include contained keyword, <> : Replace with appropriate value.'); -} - - function sendLbc(message, tipper, recipient, amount, privacyFlag) { getAddress(recipient, function (err, address) { if (err) { @@ -169,6 +174,7 @@ function getAddress(userId, cb) { }); } + function inPrivateOrBotSandbox(msg) { if ((msg.channel.type == 'dm') || (msg.channel.id === '369896313082478594')) { return true; @@ -177,6 +183,7 @@ function inPrivateOrBotSandbox(msg) { } } + function getValidatedAmount(amount) { amount = amount.trim(); if (amount.toLowerCase().endsWith('lbc')) { @@ -189,3 +196,9 @@ function getValidatedAmount(amount) { function txLink(txId) { return ""; } + + +function formatDescriptions(msgparts) { + return msgparts.map(elem => '\t' + elem[0] + '\n\t\t' + elem[1] + '\n') + .join(''); +} From 623250e1b4edd49aa7eb26ee23a36c8e7aea1091 Mon Sep 17 00:00:00 2001 From: ProfessorDey Date: Tue, 6 Feb 2018 06:18:05 +0000 Subject: [PATCH 2/8] Adding Multi User Tipping and Role Tipping This is actually not too substantial an addition. it adds 2 additional command exports for tipbot, namely !multitip and !roletip. !multitip [private] + The more complex new feature, this command will take a list of users, seperated by spaces, and passes through to doTip() if you only list a single user. It tests each word past the ! command and private tag, checking for a user mention using the regex.test() function of the Discord.js USERS_PATTERN, using that to count the number of users mentioned in a row, then takes the first word that is not a match to be the amount. Error checking then returns the appropriate errors to the user, if any. Otherwise it proceeds to send tips to each user individually. this results in messages for every user that receives a tip, which can be messy. It will also message the author once for every user a tip is sent to, if using private mode, this isn't ideal, but would require rewriting the sendLbc() function, which is outside the scope of this commit. !roletip This is a relatively simpler feature, the command taking a single role and an amount, then extracting the userIDs from the role via the Roles.members and GuildMember.user values. It will return seperate errors for a lack of a role in the message and the lack of any users in a role. Like the !multitip command, it will send tips individually to each user, with the same spam of messages either in the channel or the author's private channel. --- bot/modules/tipbot.js | 131 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index 440e457..6b133fb 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -6,7 +6,9 @@ config = config.get('lbrycrd'); const lbry = new bitcoin.Client(config); exports.commands = [ - "tip" + "tip", + "multitip", + "roletip" ] exports.tip = { usage: "", @@ -32,6 +34,46 @@ exports.tip = { } } +exports.multitip = { + usage: "", + description: '\t[help]\n\t\tGet this message\n\t+ \n\t\tMention one or more users in a row, seperated by spaces, then an amount that each mentioned user will receive\n\tprivate + \n\t\tPut private before the user list to have each user tipped privately, without revealing other users tipped\nKey: [] : Optionally include contained keyword, <> : Replace with the appropriate value, + : Value can be repeated for multiple entries', + process: async function (bot, msg, suffix) { + let tipper = msg.author.id.replace('!', ''), + words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), + subcommand = words.length >= 2 ? words[1] : 'help', + helpmsgparts = [['[help]', 'Get this message'], + ['+ ', 'Mention one or more users in a row, seperated by spaces, then an amount that each mentioned user will receive.'], + ['private + ','Put private before the user list to have each user tipped privately, without revealing other users tipped.']], + helpmsg = '```**!multitip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with the appropriate value, + : Value can be repeated for multiple entries.```', + channelwarning = 'Please use <#369896313082478594> or DMs to talk to bots.'; + switch(subcommand) { + case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; + default: doMultiTip(msg, tipper, words, helpmsg); break; + } + } +} + + +exports.roletip = { + usage: "", + description: '\t[help]\n\t\tGet this message\n\t \n\t\tMention a single role, then an amount that each user in that role will receive\n\tprivate \n\t\tPut private before the role to have each user tipped privately, without revealing other users tipped\nKey: [] : Optionally include contained keyword, <> : Replace with the appropriate value', + process: async function (bot, msg, suffix) { + let tipper = msg.author.id.replace('!', ''), + words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), + subcommand = words.length >= 2 ? words[1] : 'help', + helpmsgparts = [['[help]', 'Get this message'], + [' ', 'Mention a single role, then an amount that each user in that role will receive.'], + ['private ','Put private before the role to have each user tipped privately, without revealing other users tipped.']], + helpmsg = '```**!roletip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with the appropriate value.```', + channelwarning = 'Please use <#369896313082478594> or DMs to talk to bots.'; + switch(subcommand) { + case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; + default: doRoleTip(msg, tipper, words, helpmsg); break; + } + } +} + + function privateOrSandboxOnly(message, wrongchannelmsg, fn, args) { if (!inPrivateOrBotSandbox(message)) { message.reply(wrongchannelmsg); @@ -101,8 +143,8 @@ function doTip(message, tipper, words, helpmsg) { return; } - let prv = 0; - let amountOffset = 2; + var prv = 0; + var amountOffset = 2; if (words.length >= 4 && words[1] === 'private') { prv = 1; amountOffset = 3; @@ -124,6 +166,89 @@ function doTip(message, tipper, words, helpmsg) { } +function doMultiTip(message, tipper, words, helpmsg) { + if (!words) { + doHelp(message, helpmsg); + return; + } + if (words.length < 4) { + doTip(message, tipper, words, helpmsg); + return; + } + var prv = 0; + if (words.length >= 5 && words[1] === 'private') { + prv = 1; + } + let [userIDs, amount] = findUserIDsAndAmount(message, words, prv + 1); + if (amount == null) { + message.reply('I dont know how to tip that many credits'); + return; + } + if (!userIDs) { + message.reply('Sorry, I could not find a user in your tip...'); + return; + } + for (var i = 0; i < userIDs.length; i++) { + sendLbc(message, tipper, userIDs[i], amount, prv); + } +} + + +function doRoleTip(message, tipper, words, helpmsg) { + if (!words || words.length < 3) { + doHelp(message, helpmsg); + return; + } + var prv = 0; + var amountOffset = 2; + if (words.length >= 4 && words[1] === 'private') { + prv = 1; + amountOffset = 3; + } + let amount = getValidatedAmount(words[amountOffset]); + if (amount == null) { + message.reply('I dont know how to tip that many credits'); + return; + } + if (message.mentions.roles.first().id) { + if (message.mentions.roles.first().members.first().id) { + let userIDs = message.mentions.roles.first().members.map(member => member.user.id.replace('!', '')); + for (var i = 0; i < userIDs; i++) { + sendLbc(message, tipper, userIDs[i], amount, prv); + } + return; + } + else { + message.reply('Sorry, I could not find any users to tip in that role...'); + return; + } + } + else { + message.reply('Sorry, I could not find any roles in your tip...'); + return; + } +} + + +function findUserIDsAndAmount(message, words, startOffset) { + var idList = []; + var amount = null; + var count = 0; + + for (var i = startOffset; i < words.length; i++) { + if (message.mentions.USERS_PATTERN.test(words[i])) { + count++; + } + else { + amount = getValidatedAmount(words[i]); + if (amount == null) break; + } + } + if (count > 0) idList = message.mentions.users.first(count).forEach(function(user) { return user.id.replace('!', ''); }); + return [idList, amount]; +} + + function sendLbc(message, tipper, recipient, amount, privacyFlag) { getAddress(recipient, function (err, address) { if (err) { From ad848c3ab154e34f13c5d7798abe8634aa6977e4 Mon Sep 17 00:00:00 2001 From: aayanl Date: Tue, 6 Feb 2018 02:53:59 -0500 Subject: [PATCH 3/8] Grammar fixes and un-hardcode bot-sandbox --- bot/modules/tipbot.js | 33 +++++++++++++++++---------------- config/default.json.example | 7 ++++--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index 6b133fb..5148a6d 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -4,6 +4,7 @@ const bitcoin = require('bitcoin'); let config = require('config'); config = config.get('lbrycrd'); const lbry = new bitcoin.Client(config); +let moderation = config.get('moderation'); exports.commands = [ "tip", @@ -17,13 +18,13 @@ exports.tip = { let tipper = msg.author.id.replace('!', ''), words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), subcommand = words.length >= 2 ? words[1] : 'help', - helpmsgparts = [['[help]', 'Get this message'], - ['balance', 'Get your balance'], - ['deposit', 'Get address for your deposits'], + helpmsgparts = [['[help]', 'Get this message.'], + ['balance', 'Get your balance.'], + ['deposit', 'Get address for your deposits.'], ['withdraw ADDRESS AMOUNT', 'Withdraw AMOUNT credits to ADDRESS'], ['[private] ', 'Mention a user with @ and then the amount to tip them, or put private before the user to tip them privately.']], helpmsg = '```**!tip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with appropriate value.```', - channelwarning = 'Please use <#369896313082478594> or DMs to talk to bots.'; + channelwarning = 'Please use <'+ moderation.sandboxchannel + '> or DMs to talk to bots.'; switch (subcommand) { case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; case 'balance': doBalance(msg, tipper); break; @@ -41,11 +42,11 @@ exports.multitip = { let tipper = msg.author.id.replace('!', ''), words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), subcommand = words.length >= 2 ? words[1] : 'help', - helpmsgparts = [['[help]', 'Get this message'], + helpmsgparts = [['[help]', 'Get this message.'], ['+ ', 'Mention one or more users in a row, seperated by spaces, then an amount that each mentioned user will receive.'], ['private + ','Put private before the user list to have each user tipped privately, without revealing other users tipped.']], helpmsg = '```**!multitip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with the appropriate value, + : Value can be repeated for multiple entries.```', - channelwarning = 'Please use <#369896313082478594> or DMs to talk to bots.'; + channelwarning = 'Please use <'+ moderation.sandboxchannel + '> or DMs to talk to bots.'; switch(subcommand) { case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; default: doMultiTip(msg, tipper, words, helpmsg); break; @@ -65,7 +66,7 @@ exports.roletip = { [' ', 'Mention a single role, then an amount that each user in that role will receive.'], ['private ','Put private before the role to have each user tipped privately, without revealing other users tipped.']], helpmsg = '```**!roletip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with the appropriate value.```', - channelwarning = 'Please use <#369896313082478594> or DMs to talk to bots.'; + channelwarning = 'Please use <'+ moderation.sandboxchannel + '> or DMs to talk to bots.'; switch(subcommand) { case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; default: doRoleTip(msg, tipper, words, helpmsg); break; @@ -91,7 +92,7 @@ function doHelp(message, helpmsg) { function doBalance(message, tipper) { lbry.getBalance(tipper, 1, function (err, balance) { if (err) { - message.reply('Error getting balance'); + message.reply('Error getting balance.'); } else { message.reply('You have *' + balance + '* LBC'); @@ -103,7 +104,7 @@ function doBalance(message, tipper) { function doDeposit(message, tipper) { getAddress(tipper, function (err, address) { if (err) { - message.reply('Error getting deposit address'); + message.reply('Error getting your deposit address.'); } else { message.reply('Your address is ' + address); @@ -122,7 +123,7 @@ function doWithdraw(message, tipper, words, helpmsg) { amount = getValidatedAmount(words[3]); if (amount === null) { - message.reply('I dont know how to withdraw that many credits'); + message.reply('I dont know how to withdraw that many credits...'); return; } @@ -131,7 +132,7 @@ function doWithdraw(message, tipper, words, helpmsg) { message.reply(err.message); } else { - message.reply('You withdrew ' + amount + ' to ' + address + ' (' + txLink(txId) + ')'); + message.reply('You withdrew ' + amount + ' to ' + address + ' (' + txLink(txId) + ').'); } }); } @@ -153,7 +154,7 @@ function doTip(message, tipper, words, helpmsg) { let amount = getValidatedAmount(words[amountOffset]); if (amount === null) { - message.reply('I dont know how to tip that many credits'); + message.reply('I dont know how to tip that many credits...'); return; } @@ -181,7 +182,7 @@ function doMultiTip(message, tipper, words, helpmsg) { } let [userIDs, amount] = findUserIDsAndAmount(message, words, prv + 1); if (amount == null) { - message.reply('I dont know how to tip that many credits'); + message.reply('I don\'t know how to tip that many credits...'); return; } if (!userIDs) { @@ -207,7 +208,7 @@ function doRoleTip(message, tipper, words, helpmsg) { } let amount = getValidatedAmount(words[amountOffset]); if (amount == null) { - message.reply('I dont know how to tip that many credits'); + message.reply('I don\'t know how to tip that many credits...'); return; } if (message.mentions.roles.first().id) { @@ -234,7 +235,7 @@ function findUserIDsAndAmount(message, words, startOffset) { var idList = []; var amount = null; var count = 0; - + for (var i = startOffset; i < words.length; i++) { if (message.mentions.USERS_PATTERN.test(words[i])) { count++; @@ -301,7 +302,7 @@ function getAddress(userId, cb) { function inPrivateOrBotSandbox(msg) { - if ((msg.channel.type == 'dm') || (msg.channel.id === '369896313082478594')) { + if ((msg.channel.type == 'dm') || (msg.channel.id === moderation.sandboxchannel)) { return true; } else { return false; diff --git a/config/default.json.example b/config/default.json.example index c5d4fb5..394c832 100644 --- a/config/default.json.example +++ b/config/default.json.example @@ -14,7 +14,8 @@ "url":"mongodb://localhost:27017/wunderbot" }, "moderation":{ - "perms": ["LBRY MODS","LBRY TEAM"], // Roles that have access to all commands. - "logchannel": "371620338263523328" // Channel to log the bots moderation.. + "perms": ["LBRY MODS","LBRY TEAM"], // Roles that have access to all commands. + "logchannel": "371620338263523328", // Channel to log the bots moderation. + "sandboxchannel": "369896313082478594" // Channel for bot sandbox. } -} \ No newline at end of file +} From f239d5f394ed79d211f2f4506e7cd39581c7d649 Mon Sep 17 00:00:00 2001 From: ProfessorDey Date: Mon, 12 Feb 2018 13:48:38 +0000 Subject: [PATCH 4/8] Implement MSFTblockchain's Fixes Implements MSFTblockchain's fixes from PR #4 without customisations or version regression. Now requires Node.js Regex module for testing for user mentions in text with regex, including a fallback if a unit test fails. Also adds !tipcommands command which gives an up to date list of all commands tipbot accepts with a brief description of each. The tip message tail about DMing tipbot is now contextualised to give the appropriate ! and also points users towards the new !tipcommands command. Private tip messages are now a bit nicer to read and prv is now a boolean flag to fix some odd errors that were occuring. Error Messages now delete themselves after a short time for cleanliness. Help messages are now in Discord Embeds to line up with Wunderbot, though this currently ignores tabs, so help messages don't quite match the requested format. Commands have been made bold to prevent confusion. Everything should now actually work, but will require testing to be sure. --- bot/modules/tipbot.js | 129 +++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 46 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index 5148a6d..176ca14 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -1,19 +1,21 @@ 'use strict'; const bitcoin = require('bitcoin'); -let config = require('config'); +let config = require('config'), + spamchannel = config.get('moderation').botspamchannel, + regex = require('regex'); config = config.get('lbrycrd'); const lbry = new bitcoin.Client(config); -let moderation = config.get('moderation'); exports.commands = [ "tip", "multitip", - "roletip" + "roletip", + "tipcommands" ] exports.tip = { usage: "", - description: '\t[help]\n\t\tGet this message\n\tbalance\n\t\tGet your balance\n\tdeposit\n\t\tGet address for your deposits\n\twithdraw ADDRESS AMOUNT\n\t\tWithdraw AMOUNT credits to ADDRESS\n\t[private] \n\t\tMention a user with @ and then the amount to tip them, or put private before the user to tip them privately.\nKey: [] : Optionally include contained keyword, <> : Replace with appropriate value.', + description: 'Tip a given user with an amount of LBC or perform wallet specific operations.', process: async function (bot, msg, suffix) { let tipper = msg.author.id.replace('!', ''), words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), @@ -23,8 +25,12 @@ exports.tip = { ['deposit', 'Get address for your deposits.'], ['withdraw ADDRESS AMOUNT', 'Withdraw AMOUNT credits to ADDRESS'], ['[private] ', 'Mention a user with @ and then the amount to tip them, or put private before the user to tip them privately.']], - helpmsg = '```**!tip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with appropriate value.```', - channelwarning = 'Please use <'+ moderation.sandboxchannel + '> or DMs to talk to bots.'; + helpmsg = { "embed" : { + "description": formatDescriptions(helpmsgparts) + + '\nKey: [] : Optionally include contained keyword, <> : Replace with appropriate value.', + "color": 1109218, + "author": { "name": "!tip" } } }, + channelwarning = 'Please use <'+ spamchannel + '> or DMs to talk to bots.'; switch (subcommand) { case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; case 'balance': doBalance(msg, tipper); break; @@ -37,7 +43,7 @@ exports.tip = { exports.multitip = { usage: "", - description: '\t[help]\n\t\tGet this message\n\t+ \n\t\tMention one or more users in a row, seperated by spaces, then an amount that each mentioned user will receive\n\tprivate + \n\t\tPut private before the user list to have each user tipped privately, without revealing other users tipped\nKey: [] : Optionally include contained keyword, <> : Replace with the appropriate value, + : Value can be repeated for multiple entries', + description: 'Tip multiple users simultaneously for the same amount of LBC each.', process: async function (bot, msg, suffix) { let tipper = msg.author.id.replace('!', ''), words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), @@ -45,8 +51,12 @@ exports.multitip = { helpmsgparts = [['[help]', 'Get this message.'], ['+ ', 'Mention one or more users in a row, seperated by spaces, then an amount that each mentioned user will receive.'], ['private + ','Put private before the user list to have each user tipped privately, without revealing other users tipped.']], - helpmsg = '```**!multitip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with the appropriate value, + : Value can be repeated for multiple entries.```', - channelwarning = 'Please use <'+ moderation.sandboxchannel + '> or DMs to talk to bots.'; + helpmsg = { "embed" : { + "description": formatDescriptions(helpmsgparts) + + '\nKey: [] : Optionally include contained keyword, <> : Replace with appropriate value, + : Value can be repeated for multiple entries.', + "color": 1109218, + "author": { "name": "!multitip" } } }, + channelwarning = 'Please use <'+ spamchannel + '> or DMs to talk to bots.'; switch(subcommand) { case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; default: doMultiTip(msg, tipper, words, helpmsg); break; @@ -57,7 +67,7 @@ exports.multitip = { exports.roletip = { usage: "", - description: '\t[help]\n\t\tGet this message\n\t \n\t\tMention a single role, then an amount that each user in that role will receive\n\tprivate \n\t\tPut private before the role to have each user tipped privately, without revealing other users tipped\nKey: [] : Optionally include contained keyword, <> : Replace with the appropriate value', + description: 'Tip every user in a given role the same amount of LBC.', process: async function (bot, msg, suffix) { let tipper = msg.author.id.replace('!', ''), words = msg.content.trim().split(' ').filter(function (n) { return n !== ""; }), @@ -65,8 +75,12 @@ exports.roletip = { helpmsgparts = [['[help]', 'Get this message'], [' ', 'Mention a single role, then an amount that each user in that role will receive.'], ['private ','Put private before the role to have each user tipped privately, without revealing other users tipped.']], - helpmsg = '```**!roletip**\n' + formatDescriptions(helpmsgparts) + 'Key: [] : Optionally include contained keyword, <> : Replace with the appropriate value.```', - channelwarning = 'Please use <'+ moderation.sandboxchannel + '> or DMs to talk to bots.'; + helpmsg = { "embed" : { + "description": formatDescriptions(helpmsgparts) + + '\nKey: [] : Optionally include contained keyword, <> : Replace with appropriate value.', + "color": 1109218, + "author": { "name": "!roletip" } } }, + channelwarning = 'Please use <'+ spamchannel + '> or DMs to talk to bots.'; switch(subcommand) { case 'help': privateOrSandboxOnly(msg, channelwarning, doHelp, [helpmsg]); break; default: doRoleTip(msg, tipper, words, helpmsg); break; @@ -74,6 +88,22 @@ exports.roletip = { } } +exports.tipcommands = { + usage: "", + description: 'Lists all available tipbot commands with brief descriptions for each one.', + process: async function (bot, msg, suffix) { + let helpmsgparts = [['!tip', 'Tip a given user with an amount of LBC or perform wallet specific operations.'], + ['!multitip', 'Tip multiple users simultaneously for the same amount of LBC each.'], + ['!roletip','Tip every user in a given role the same amount of LBC.'], + ['!tipcommands', 'Lists all available tipbot commands with brief descriptions for each one.']], + helpmsg = { "embed" : { + "description": "These are all the commands that TipBot currently supports. Use `! help` for usage instructions.\n" + + formatDescriptions(helpmsgparts), + "color": 1109218, + "author": { "name": "Tipbot Commands" } } }; + msg.reply(helpmsg); + } +} function privateOrSandboxOnly(message, wrongchannelmsg, fn, args) { if (!inPrivateOrBotSandbox(message)) { @@ -92,7 +122,7 @@ function doHelp(message, helpmsg) { function doBalance(message, tipper) { lbry.getBalance(tipper, 1, function (err, balance) { if (err) { - message.reply('Error getting balance.'); + message.reply('Error getting balance.').then(message => message.delete(5000)); } else { message.reply('You have *' + balance + '* LBC'); @@ -104,7 +134,7 @@ function doBalance(message, tipper) { function doDeposit(message, tipper) { getAddress(tipper, function (err, address) { if (err) { - message.reply('Error getting your deposit address.'); + message.reply('Error getting your deposit address.').then(message => message.delete(5000)); } else { message.reply('Your address is ' + address); @@ -123,16 +153,16 @@ function doWithdraw(message, tipper, words, helpmsg) { amount = getValidatedAmount(words[3]); if (amount === null) { - message.reply('I dont know how to withdraw that many credits...'); + message.reply('I dont know how to withdraw that many coins...').then(message => message.delete(5000)); return; } lbry.sendFrom(tipper, address, amount, function (err, txId) { if (err) { - message.reply(err.message); + message.reply(err.message).then(message => message.delete(5000)); } else { - message.reply('You withdrew ' + amount + ' to ' + address + ' (' + txLink(txId) + ').'); + message.reply('You withdrew ' + amount + ' LBC to ' + address + '.\n' + txLink(txId)); } }); } @@ -144,25 +174,25 @@ function doTip(message, tipper, words, helpmsg) { return; } - var prv = 0; + var prv = false; var amountOffset = 2; if (words.length >= 4 && words[1] === 'private') { - prv = 1; + prv = true; amountOffset = 3; } let amount = getValidatedAmount(words[amountOffset]); if (amount === null) { - message.reply('I dont know how to tip that many credits...'); + message.reply('I dont know how to tip that many coins...').then(message => message.delete(5000)); return; } if (message.mentions.users.first().id) { - sendLbc(message, tipper, message.mentions.users.first().id.replace('!', ''), amount, prv); + sendLBC(message, tipper, message.mentions.users.first().id.replace('!', ''), amount, prv); } else { - message.reply('Sorry, I could not find a user in your tip...'); + message.reply('Sorry, I could not find a user in your tip...').then(message => message.delete(5000)); } } @@ -176,21 +206,21 @@ function doMultiTip(message, tipper, words, helpmsg) { doTip(message, tipper, words, helpmsg); return; } - var prv = 0; + var prv = false; if (words.length >= 5 && words[1] === 'private') { - prv = 1; + prv = true; } - let [userIDs, amount] = findUserIDsAndAmount(message, words, prv + 1); + let [userIDs, amount] = findUserIDsAndAmount(message, words, prv); if (amount == null) { - message.reply('I don\'t know how to tip that many credits...'); + message.reply('I don\'t know how to tip that many coins...').then(message => message.delete(5000)); return; } if (!userIDs) { - message.reply('Sorry, I could not find a user in your tip...'); + message.reply('Sorry, I could not find a user in your tip...').then(message => message.delete(5000)); return; } for (var i = 0; i < userIDs.length; i++) { - sendLbc(message, tipper, userIDs[i], amount, prv); + sendLBC(message, tipper, userIDs[i], amount, prv); } } @@ -200,44 +230,48 @@ function doRoleTip(message, tipper, words, helpmsg) { doHelp(message, helpmsg); return; } - var prv = 0; + var prv = false; var amountOffset = 2; if (words.length >= 4 && words[1] === 'private') { - prv = 1; + prv = true; amountOffset = 3; } let amount = getValidatedAmount(words[amountOffset]); if (amount == null) { - message.reply('I don\'t know how to tip that many credits...'); + message.reply('I don\'t know how to tip that many coins...').then(message => message.delete(5000)); return; } if (message.mentions.roles.first().id) { if (message.mentions.roles.first().members.first().id) { let userIDs = message.mentions.roles.first().members.map(member => member.user.id.replace('!', '')); for (var i = 0; i < userIDs; i++) { - sendLbc(message, tipper, userIDs[i], amount, prv); + sendLBC(message, tipper, userIDs[i], amount, prv); } return; } else { - message.reply('Sorry, I could not find any users to tip in that role...'); + message.reply('Sorry, I could not find any users to tip in that role...').then(message => message.delete(5000)); return; } } else { - message.reply('Sorry, I could not find any roles in your tip...'); + message.reply('Sorry, I could not find any roles in your tip...').then(message => message.delete(5000)); return; } } -function findUserIDsAndAmount(message, words, startOffset) { +function findUserIDsAndAmount(message, words, prv) { var idList = []; var amount = null; var count = 0; + var startOffset = 1; + if (prv) startOffset = 2; + var userPattern = message.mentions.USERS_PATTERN; + if (!userPattern || !userPattern.test('<@123456789>')) userPattern = new Regex(/<@!?[0-9]+>/); for (var i = startOffset; i < words.length; i++) { - if (message.mentions.USERS_PATTERN.test(words[i])) { + if (userPattern.test(words[i])) { count++; } else { @@ -253,24 +287,27 @@ function findUserIDsAndAmount(message, words, startOffset) { function sendLbc(message, tipper, recipient, amount, privacyFlag) { getAddress(recipient, function (err, address) { if (err) { - message.reply(err.message); + message.reply(err.message).then(message => message.delete(5000)); } else { lbry.sendFrom(tipper, address, amount, 1, null, null, function (err, txId) { if (err) { - message.reply(err.message); + message.reply(err.message).then(message => message.delete(5000)); } else { - var imessage = - 'Wubba lubba dub dub! <@' + tipper + '> tipped <@' + recipient + '> ' + amount + ' LBC (' + txLink(txId) + '). ' + - 'DM me `!tip` for tipbot instructions.' + var tx = txLink(txId); + var msgtail = '\nDM me with `' + message.content.split(" ", 1)[0] + '` for command specific instructions or with `!tipcommands` for all available commands'; if (privacyFlag) { - message.author.send(imessage); + var authmsg = 'You have just privately tipped <@' + recipient + '> ' + amount + ' LBC.\n' + tx + msgtail; + message.author.send(authmsg); if (message.author.id != message.mentions.users.first().id) { - message.mentions.users.first().send(imessage); + var recipientmsg = 'You have just been privately tipped ' + amount + ' LBC by <@' + tipper + '>.\n' + tx + msgtail; + message.mentions.users.first().send(recipientmsg); } } else { - message.reply(imessage); + var generalmsg = + 'Wubba lubba dub dub! <@' + tipper + '> tipped <@' + recipient + '> ' + amount + ' LBC.\n' + tx + msgtail; + message.reply(generalmsg); } } }); @@ -302,7 +339,7 @@ function getAddress(userId, cb) { function inPrivateOrBotSandbox(msg) { - if ((msg.channel.type == 'dm') || (msg.channel.id === moderation.sandboxchannel)) { + if ((msg.channel.type == 'dm') || (msg.channel.id === spamchannel)) { return true; } else { return false; @@ -325,6 +362,6 @@ function txLink(txId) { function formatDescriptions(msgparts) { - return msgparts.map(elem => '\t' + elem[0] + '\n\t\t' + elem[1] + '\n') + return msgparts.map(elem => '\t**' + elem[0] + '**\n\t\t' + elem[1] + '\n') .join(''); } From b6598a4ffa5acbd4fb2a46b125984570b395da56 Mon Sep 17 00:00:00 2001 From: ProfessorDey Date: Mon, 12 Feb 2018 22:43:37 +0000 Subject: [PATCH 5/8] Final functionality fixes from MSFTblockchain All finally operational and tested on MSFT's RVN server, code ported back over after figuring out various JS specific implementation issues. Now all works correctly and doesn't include anyone it shouldn't. --- bot/modules/tipbot.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index 176ca14..c7f0748 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -220,7 +220,7 @@ function doMultiTip(message, tipper, words, helpmsg) { return; } for (var i = 0; i < userIDs.length; i++) { - sendLBC(message, tipper, userIDs[i], amount, prv); + sendLBC(message, tipper, userIDs[i].toString(), amount, prv); } } @@ -267,24 +267,22 @@ function findUserIDsAndAmount(message, words, prv) { var count = 0; var startOffset = 1; if (prv) startOffset = 2; - var userPattern = message.mentions.USERS_PATTERN; - if (!userPattern || !userPattern.test('<@123456789>')) userPattern = new Regex(/<@!?[0-9]+>/); - + var regex = new RegExp(/<@!?[0-9]+>/) for (var i = startOffset; i < words.length; i++) { - if (userPattern.test(words[i])) { + if (regex.test(words[i])) { count++; - } - else { - amount = getValidatedAmount(words[i]); + idList.push(words[i].match(/[0-9]+/)); + } else { + amount = getValidatedAmount(words[Number(count)+1]); if (amount == null) break; + break } } - if (count > 0) idList = message.mentions.users.first(count).forEach(function(user) { return user.id.replace('!', ''); }); return [idList, amount]; } -function sendLbc(message, tipper, recipient, amount, privacyFlag) { +function sendLBC(message, tipper, recipient, amount, privacyFlag) { getAddress(recipient, function (err, address) { if (err) { message.reply(err.message).then(message => message.delete(5000)); From 8eb5bb9578d3fe86a233ada7479874de5bf9f597 Mon Sep 17 00:00:00 2001 From: ProfessorDey Date: Wed, 21 Feb 2018 19:52:46 +0000 Subject: [PATCH 6/8] Integrating final MSFT fixes Now the last little changes have been implemented, had to modify how we got the user to message since we were previously getting the first user mentioned in the message, whereas we now match against all known users in the guild. This means users actually get messaged by, say, rolebot even if they're not in the channel you're tipping from. Of course you can't directly mention anyone not in the channel for multitip, so that's a non-issue in most use cases. Also had to ensure certain function calls were sending the right type, because dynamic languages and lack of type safety. That _should_ be absolutely everything needed to have it fully functional. --- bot/modules/tipbot.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index c7f0748..3d0112c 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -274,8 +274,7 @@ function findUserIDsAndAmount(message, words, prv) { idList.push(words[i].match(/[0-9]+/)); } else { amount = getValidatedAmount(words[Number(count)+1]); - if (amount == null) break; - break + break; } } return [idList, amount]; @@ -283,12 +282,12 @@ function findUserIDsAndAmount(message, words, prv) { function sendLBC(message, tipper, recipient, amount, privacyFlag) { - getAddress(recipient, function (err, address) { + getAddress(recipient.toString(), function (err, address) { if (err) { message.reply(err.message).then(message => message.delete(5000)); } else { - lbry.sendFrom(tipper, address, amount, 1, null, null, function (err, txId) { + lbry.sendFrom(tipper, address, Number(amount), 1, null, null, function (err, txId) { if (err) { message.reply(err.message).then(message => message.delete(5000)); } @@ -299,8 +298,9 @@ function sendLBC(message, tipper, recipient, amount, privacyFlag) { var authmsg = 'You have just privately tipped <@' + recipient + '> ' + amount + ' LBC.\n' + tx + msgtail; message.author.send(authmsg); if (message.author.id != message.mentions.users.first().id) { + var usr = message.guild.members.find('id', recipient).user; var recipientmsg = 'You have just been privately tipped ' + amount + ' LBC by <@' + tipper + '>.\n' + tx + msgtail; - message.mentions.users.first().send(recipientmsg); + usr.send(recipientmsg); } } else { var generalmsg = From 6cfd4f578eee5432677845626bc01177daa88d94 Mon Sep 17 00:00:00 2001 From: Niko Storni Date: Thu, 22 Feb 2018 02:26:35 +0100 Subject: [PATCH 7/8] updated default configuration --- config/default.json.example | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/config/default.json.example b/config/default.json.example index 394c832..2aab9d5 100644 --- a/config/default.json.example +++ b/config/default.json.example @@ -1,21 +1,13 @@ { - // Bot configuration "bot": { - "token":"discordbottoken", - "prefix": "!", - "debug": false + "token": "discordbottoken", + "prefix": "!", + "debug": false }, "lbrycrd": { - "port": 9245, - "user": "lbry", - "pass": "lbry" + "port": 9245, + "user": "lbry", + "pass": "lbry" }, - "mongodb": { - "url":"mongodb://localhost:27017/wunderbot" - }, - "moderation":{ - "perms": ["LBRY MODS","LBRY TEAM"], // Roles that have access to all commands. - "logchannel": "371620338263523328", // Channel to log the bots moderation. - "sandboxchannel": "369896313082478594" // Channel for bot sandbox. - } + "sandboxchannel": "#369896313082478594" } From 8a29b053a071056b0d4c95c3e6b15c7f81c035af Mon Sep 17 00:00:00 2001 From: Niko Storni Date: Thu, 22 Feb 2018 02:35:45 +0100 Subject: [PATCH 8/8] missed 2 coins -> credits --- bot/modules/tipbot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/modules/tipbot.js b/bot/modules/tipbot.js index 9882dcf..53357fb 100644 --- a/bot/modules/tipbot.js +++ b/bot/modules/tipbot.js @@ -241,7 +241,7 @@ function doMultiTip(message, tipper, words, helpmsg) { } let [userIDs, amount] = findUserIDsAndAmount(message, words, prv); if (amount == null) { - message.reply("I don't know how to tip that many coins...").then(message => message.delete(5000)); + message.reply("I don't know how to tip that many credits...").then(message => message.delete(5000)); return; } if (!userIDs) { @@ -266,7 +266,7 @@ function doRoleTip(message, tipper, words, helpmsg) { } let amount = getValidatedAmount(words[amountOffset]); if (amount == null) { - message.reply("I don't know how to tip that many coins...").then(message => message.delete(5000)); + message.reply("I don't know how to tip that many credits...").then(message => message.delete(5000)); return; } if (message.mentions.roles.first().id) {