Started with adding plugins to the new wunderbot, as this is quite a big update this commit contains:

* Remade commands: price, rank, userinfo,hash,lbrylink, releasenotes, stats, supportbot.
* Added modlogs and settings for modlogs.
* Commands can now be locked to channels with the locktochannel module.
* customCommand/triggers can be saved in mongodb and edited on the fly, the bot will always use the latest messages.

This commit does not contain:
* A working claimbot, the new implantation with chainquery is on the way, this will be a seperate issue.
* A working webinterface, therefor, some items does not work, this will be a seperate issue.

This means that this code should remain in the wunderbot-v2 branch until a working webinterface where we can control the custom triggers/commands and other settings for the bot.
This commit is contained in:
filipnyquist 2018-07-18 15:41:00 +02:00
parent 486fb43b73
commit 6a13432247
57 changed files with 4770 additions and 7825 deletions

View file

@ -1,3 +1,12 @@
{
"presets": ["node8"]
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}

12
.editorconfig Normal file
View file

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

4
.eslintignore Normal file
View file

@ -0,0 +1,4 @@
# /node_modules/* and /bower_components/* ignored by default
# Ignore built files except build/index.js
dist/*

41
.eslintrc Normal file
View file

@ -0,0 +1,41 @@
{
"parser": "babel-eslint",
"env": {
"node": true
},
"extends": ["airbnb-base", "prettier"],
"rules": {
"func-names": 0,
"max-len": [1, 240],
"no-unused-vars": 0,
"no-script-url": 0,
"no-unused-expressions":"off",
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"no-console": 0,
"no-undef": 0,
"import/prefer-default-export": 0,
"global-require": 0,
"comma-dangle": ["off"],
"import/extensions": 0,
"guard-for-in": ["warn"],
"no-restricted-syntax": ["off"],
"object-curly-spacing": ["off"],
"padded-blocks": ["off"],
"no-underscore-dangle": [
"error",
{
"allowAfterThis": true
}
],
"no-plusplus": 0,
"no-shadow": 0,
"class-methods-use-this": 0,
"consistent-return": 0,
"indent": ["error", 4],
"eol-last": [1, "always"]
},
"parserOptions": {
"ecmaVersion": 6
}
}

23
.gitignore vendored
View file

@ -1,15 +1,9 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# dist
dist/*
#configuration
config/default.json
# Runtime data
pids
*.pid
@ -34,7 +28,7 @@ bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
@ -60,4 +54,17 @@ typings/
.yarn-integrity
# dotenv environment variables file
.env
.env
dist/
dist/*
.vscode/
.idea
.idea/*
###################################
# Ignore config directory
config/*
# Besides examples
!config/*.example.json

28
LICENSE
View file

@ -1,15 +1,21 @@
The MIT License (MIT)
MIT License
Copyright (c) 2016-2018 LBRY Inc
Copyright (c) 2018 LBRYio
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

109
README.md
View file

@ -1,107 +1,2 @@
## Bot for [LBRY's Discord](https://discord.gg/tgnNHf5)
(This README will be updated along with bot updates)
<h3>
<details><summary>Features:</summary>
* Price bot displays price of lbc for currency given.
- *Responds to `!price <cur> <amount>`*
* Stats bot displays current market stats of lbc.
- *Responds to `!stats`*
* Hash bot displays current hashrate of lbc network. Also Includes to calculate given MH/s to LBC & fiat per hr, day, week, month.
- *Responds to `!hash`*
- *Responds to `!hash power <MH/s> <fiat>`*
* AltStats bot displays current market stats of specfied currency
- *Responds to `!altstats <coin>`*
* AltPrice bot displays current price for given coin and currency
- *Responds to `!altprice <coin> <currency> <amount>`*
* Github Release Notes bot displays release notes for current LBRY Desktop release.
- *Responds to `!releasenotes`*
- *(moderator only) `!releasenotes post` - send to release notes channel*
* Purge Bot deletes X amount of messages.
- *(moderator only) Responds to `!purge <X>`*
* Speech bot displays top claim from provided image name(coming soon posting to
speech).
- *Responds to `!speech <imagename>`*
* Welcome bot sends Direct Message when new users join,
- *(moderator only) Responds to `!welcome <@username>`*
* Timeout bot assigns members the timeout roll for X-minutes where they are restricted from talking
- *(moderator only) Responds to `!timeout <@username> <Mins>`*
* Roll Setter bot allows users to assign themselves rolls
- *Responds to `!addrole <role>` - Adds to Role*
- *Responds to `!delrole <role>` - Deletes from Role*
- *Responds to `!roles` - List Available Roles*
* LBRY URL Linker displays lbry:// urls as open.lbry.io links to make them clickable
* LBRY claim bot displays recent publishes on the lbry protocol
* IRC bot to connect an irc channel with discord
* Spam Detection Bot to Prevent Discord Raids and Spammers
* Dynamic plugin loading with permission support.
</details>
</h3>
____
### Requirements
* node > 8.0.0
* npm > 0.12.x
* yarn ( install with npm install -g yarn if not installed )
____
### Installation
Create a bot and get the bot's API Token:
https://discordapp.com/developers/applications/me
Edit and rename default.json.example in /config, then cd to wunderbot directory
and run:
```
yarn install
node bot/bot.js
```
____
### Development
Be sure to run the command below before working on any code, this ensures
prettier goes to work and keeps code to our standard.
```
yarn install --production=false
```
to run prettier before submitting your code simply run the following in the bots root directory.
```
yarn precommit
```
# wunderbot
A blazing fast discord bot

View file

@ -1,215 +0,0 @@
'use strict';
// Load up libraries
const Discord = require('discord.js');
// Load config!
let config = require('config');
config = config.get('bot');
//load modules
const claimbot = require('./modules/claimbot.js');
const commandsV2 = require('./modules/commandsV2.js');
const supportbot = require('./modules/supportbot.js');
let aliases;
try {
aliases = require('./alias.json');
} catch (e) {
console.log('No aliases defined');
//No aliases defined
aliases = {
test: {
process: function(bot, msg) {
msg.channel.send('test');
}
}
};
}
let commands = {
ping: {
description: 'responds pong, useful for checking if bot is alive',
process: async function(bot, msg, suffix) {
let m = await msg.channel.send(msg.author + ' Ping?');
m.edit(`Pong! Latency is ${m.createdTimestamp - msg.createdTimestamp}ms. API Latency is ${Math.round(bot.ping)}ms`);
if (suffix) {
msg.channel.send('note that !ping takes no arguments!');
}
}
}
};
let bot = new Discord.Client();
bot.on('ready', function() {
console.log('Logged in! Serving in ' + bot.guilds.array().length + ' servers');
require('./plugins.js').init();
console.log('type ' + config.prefix + 'help in Discord for a commands list.');
bot.user.setActivity(config.prefix + 'help', { type: 'LISTENING' }).catch(console.error);
//initialize the claimbot (content bot)
claimbot.init(bot);
//initialize the commandsBot
commandsV2.init(bot);
//initialize the support bot
supportbot.init(bot);
});
process.on('unhandledRejection', err => {
console.log('unhandledRejection: ' + err);
process.exit(1); //exit node.js with an error
});
bot.on('disconnected', function() {
console.log('Disconnected!');
process.exit(1); //exit node.js with an error
});
bot.on('error', function(error) {
console.log('error: ' + error);
process.exit(1); //exit node.js with an error
});
function checkMessageForCommand(msg, isEdit) {
//check if message is a command
if (msg.author.id != bot.user.id && msg.content.startsWith(config.prefix)) {
//check if user is Online
if (!msg.author.presence.status || msg.author.presence.status == 'offline' || msg.author.presence.status == 'invisible') {
msg.author.send('Please set your Discord Presence to Online to talk to the bot!').catch(function(error) {
msg.channel
.send(
msg.author +
', Please enable Direct Messages from server members to communicate fully with our bot, ' +
'it is located in the user setting area under Privacy & Safety tab, ' +
'select the option allow direct messages from server members'
)
.then(msg.channel.send('Please set your Discord Presence to Online to talk to the Bot!'));
return;
});
}
let cmdTxt = msg.content.split(' ')[0].substring(config.prefix.length);
let suffix = msg.content.substring(cmdTxt.length + config.prefix.length + 1); //add one for the ! and one for the space
if (msg.isMentioned(bot.user)) {
try {
cmdTxt = msg.content.split(' ')[1];
suffix = msg.content.substring(bot.user.mention().length + cmdTxt.length + config.prefix.length + 1);
} catch (e) {
//no command
msg.channel.send('Yes?');
return;
}
}
let cmd = aliases.hasOwnProperty(cmdTxt) ? commands[aliases[cmdTxt]] : commands[cmdTxt];
let alias = aliases[cmdTxt];
if (alias) {
cmd = alias;
} else {
cmd = commands[cmdTxt];
}
if (cmdTxt === 'help') {
//help is special since it iterates over the other commands
if (suffix) {
let cmds = suffix.split(' ').filter(function(cmd) {
return commands[cmd];
});
let info = '';
for (let i = 0; i < cmds.length; i++) {
let cmd = cmds[i];
info += '**' + config.prefix + cmd + '**';
let usage = commands[cmd].usage;
if (usage) {
info += ' ' + usage;
}
let description = commands[cmd].description;
if (description instanceof Function) {
description = description();
}
if (description) {
info += '\n\t' + description;
}
info += '\n';
}
msg.channel.send(info);
} else {
msg.author.send('**Available Commands:**').then(function() {
let batch = '';
let sortedCommands = Object.keys(commands).sort();
for (let i in sortedCommands) {
let cmd = sortedCommands[i];
let info = '**' + config.prefix + cmd + '**';
let usage = commands[cmd].usage;
if (usage) {
info += ' ' + usage;
}
let description = commands[cmd].description;
if (description instanceof Function) {
description = description();
}
if (description) {
info += '\n\t' + description;
}
let newBatch = batch + '\n' + info;
if (newBatch.length > 1024 - 8) {
//limit message length
msg.author.send(batch);
batch = info;
} else {
batch = newBatch;
}
}
if (batch.length > 0) {
msg.author.send(batch);
}
});
}
} else if (cmd) {
// Add permission check here later on ;)
console.log('treating ' + msg.content + ' from UserID:' + msg.author + ' || UserName: ' + msg.author.username + ' as command');
try {
cmd.process(bot, msg, suffix, isEdit);
} catch (e) {
let msgTxt = 'command ' + cmdTxt + ' failed :(';
if (config.debug) {
msgTxt += '\n' + e.stack;
}
msg.channel.send(msgTxt);
}
} else {
//msg.channel.send(cmdTxt + ' not recognized as a command!').then(message => message.delete(10000));
}
} else {
//message isn't a command or is from us
//drop our own messages to prevent feedback loops
if (msg.author == bot.user) {
return;
}
if (msg.author != bot.user && msg.isMentioned(bot.user)) {
msg.channel.send('yes?'); //using a mention here can lead to looping
} else {
}
}
}
bot.on('message', msg => checkMessageForCommand(msg, false));
bot.on('messageUpdate', (oldMessage, newMessage) => {
checkMessageForCommand(newMessage, true);
});
exports.addCommand = function(commandName, commandObject) {
try {
commands[commandName] = commandObject;
} catch (err) {
console.log(err);
}
};
exports.addCustomFunc = function(customFunc) {
try {
customFunc(bot);
} catch (err) {
console.log(err);
}
};
exports.commandCount = function() {
return Object.keys(commands).length;
};
bot.login(config.token);

View file

@ -1,47 +0,0 @@
let config = require('config');
let permRanks = config.get('moderation');
let speechBotChannels = config.get('speechbot');
let priceBotChannels = config.get('pricebot');
let ExcludedSpam = config.get('spamdetection');
let hashBotChannels = config.get('hashbot');
let statsBotChannels = config.get('statsbot');
// Checks if user is allowed to use a command only for mods/team members
exports.hasPerms = function(msg) {
return msg.member.roles !== null && msg.member.roles.some(r => permRanks.perms.includes(r.name));
};
// Check if command was sent in dm
exports.inPrivate = function(msg) {
return msg.channel.type == 'dm';
};
// Checks if Message was sent from a channel in speechBot Channels list
exports.hasSpeechBotChannels = function(msg) {
return speechBotChannels.channels.includes(msg.channel.id);
};
// Checks if Message was sent from a channel in priceBot Channels list
exports.hasPriceBotChannels = function(msg) {
return priceBotChannels.channels.includes(msg.channel.id);
};
// Checks if Message was sent from a Excluded channel
exports.hasExcludedSpamChannels = function(msg) {
return ExcludedSpam.channels.includes(msg.channel.id);
};
// Checks if Message was sent from a Excluded user
exports.hasExcludedSpamUsers = function(msg) {
return ExcludedSpam.users.includes(msg.author.id);
};
// Checks if Message was sent from a channel in hashBot Channels list
exports.hasHashBotChannels = function(msg) {
return hashBotChannels.channels.includes(msg.channel.id);
};
// Checks if Message was sent from a channel in statsBot Channels list
exports.hasStatsBotChannels = function(msg) {
retrun(statsBotChannels.channels.includes(msg.channel.id));
};

View file

@ -1,71 +0,0 @@
let needle = require('needle');
let hasPriceBotChannels = require('../helpers.js').hasPriceBotChannels;
let inPrivate = require('../helpers.js').inPrivate;
let config = require('config');
let ChannelID = config.get('pricebot').mainchannel;
exports.commands = ['altprice'];
exports.altprice = {
usage: '<coin> <fiat/coin> <amount>',
description: 'display price of specified alt coin from crypto compare\n**Example:** *!altprice ETH USD 100*',
process: function(bot, msg, suffix) {
let dt = new Date();
let timestamp = dt.toUTCString();
if (!hasPriceBotChannels(msg) && !inPrivate(msg)) {
msg.channel.send('Please use <#' + ChannelID + '> or DMs to talk to price bot.');
return;
}
if (suffix !== '') {
words = suffix
.trim()
.split(' ')
.filter(function(n) {
return n !== '';
});
let currency1 = words[0].toUpperCase();
if (words[1] == undefined) {
let currency2 = 'BTC';
} else {
let currency2 = words[1].toUpperCase();
}
if (words[2] == undefined) {
let amount = '1';
} else {
if (getValidatedAmount(words[2]) === null) {
msg.reply('Please specify a number for <amount>');
return;
}
let amount = words[2].toUpperCase();
}
} else {
let currency1 = 'BTC';
let currency2 = 'USD';
let amount = '1';
}
needle.get('https://min-api.cryptocompare.com/data/all/coinlist', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('coinmarketcap API is not available');
} else {
if (!response.body.Data.hasOwnProperty(currency1)) {
msg.channel.send('Invalid Alt Coin');
return;
}
needle.get('https://min-api.cryptocompare.com/data/price?fsym=' + currency1 + '&tsyms=' + currency2, function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('coinmarketcap API is not available');
} else {
let price = Number(response.body[currency2]);
let newprice = price * amount;
let message = amount + ' ' + currency1 + ' = ' + newprice.toFixed(8) + ' ' + currency2 + '\n' + '*Updated: ' + timestamp + '*';
msg.channel.send(message);
}
});
}
});
function getValidatedAmount(amount) {
amount = amount.trim();
return amount.match(/^[0-9]+(\.[0-9]+)?$/) ? amount : null;
}
}
};

View file

@ -1,285 +0,0 @@
'use strict';
let lbry;
let mongo;
let discordBot;
let moment = require('moment');
let request = require('request');
let sleep = require('sleep');
let config = require('config');
let channels = config.get('claimbot').channels;
const Discord = require('discord.js');
module.exports = {
init: init
};
function init(discordBot_) {
if (lbry) {
throw new Error('init was already called once');
}
discordBot = discordBot_;
const MongoClient = require('mongodb').MongoClient;
MongoClient.connect(config.get('mongodb').url, function(err, db) {
if (err) {
throw err;
}
mongo = db;
const bitcoin = require('bitcoin');
lbry = new bitcoin.Client(config.get('lbrycrd'));
console.log('Activating claimbot ');
discordBot.channels.get(channels[0]).send('activating claimbot');
setInterval(function() {
announceNewClaims();
}, 60 * 1000);
announceNewClaims();
});
}
function announceNewClaims() {
if (!mongo) {
discordPost('Failed to connect to mongo', {});
return;
}
if (!lbry) {
discordPost('Failed to connect to lbrycrd', {});
return;
}
Promise.all([getLastBlock(), lbryCall('getinfo')])
.then(function([lastProcessedBlock, currentBlockInfo]) {
const currentHeight = currentBlockInfo['blocks'];
console.log(currentHeight);
if (lastProcessedBlock === null) {
console.log('First run. Setting last processed block to ' + currentHeight + ' and exiting.');
return setLastBlock(currentHeight);
}
const testBlock = false;
if (testBlock || lastProcessedBlock < currentHeight) {
const firstBlockToProcess = testBlock || lastProcessedBlock + 1,
lastBlockToProcess = testBlock || currentHeight;
console.log('Doing blocks ' + firstBlockToProcess + ' to ' + lastBlockToProcess);
return announceClaimsLoop(firstBlockToProcess, lastBlockToProcess, currentHeight);
}
})
.catch(function(err) {
discordPost(err.stack, {});
});
}
function announceClaimsLoop(block, lastBlock, currentHeight) {
let claimsFound = 0;
return lbryCall('getblockhash', block)
.then(function(blockHash) {
return lbryCall('getblock', blockHash);
})
.then(function(blockData) {
return Promise.all(blockData['tx'].map(getClaimsForTxid));
})
.then(function(arrayOfClaimArrays) {
const claims = Array.prototype.concat(...arrayOfClaimArrays).filter(function(c) {
return !!c;
});
console.log('Found ' + claims.length + ' claims in ' + block);
claimsFound = claims.length;
return Promise.all(
claims.map(function(claim) {
//the API has a rate limit. to avoid hitting it we must have a small delay between each message
//if claims were found in this block, then we wait, otherwise we don't
if (claimsFound > 0 && claim.hasOwnProperty('claimId')) sleep.msleep(300);
return announceClaim(claim, block, currentHeight);
})
);
})
.then(function() {
return setLastBlock(block);
})
.then(function() {
const nextBlock = block + 1;
if (nextBlock <= lastBlock) {
return announceClaimsLoop(nextBlock, lastBlock, currentHeight);
}
});
}
function announceClaim(claim, claimBlockHeight, currentHeight) {
console.log('' + claimBlockHeight + ': New claim for ' + claim['name']);
console.log(claim);
//ignore supports for now
//the issue with supports is that they should be treated completely differently
//they are not new claims...
if (claim.hasOwnProperty('supported claimId')) return;
let options = {
method: 'GET',
url: 'http://127.0.0.1:5000/claim_decode/' + claim['name']
};
request(options, function(error, response, body) {
if (error) throw new Error(error);
try {
console.log(JSON.stringify(JSON.parse(body), null, 2));
let claimData = null;
let channelName = null;
try {
body = JSON.parse(body);
if (body.hasOwnProperty('stream') && body.stream.hasOwnProperty('metadata')) {
claimData = body.stream.metadata;
channelName = body.hasOwnProperty('channel_name') ? body['channel_name'] : null;
}
} catch (e) {
console.error(e);
return;
}
return Promise.all([lbryCall('getvalueforname', claim['name']), lbryCall('getclaimsforname', claim['name'])]).then(function([currentWinningClaim, claimsForName]) {
//console.log(JSON.stringify(claimData));
let value = null;
if (claimData !== null) value = claimData;
else {
try {
value = JSON.parse(claim['value']);
} catch (e) {}
}
const text = [];
if (value) {
/*
if (channelName) {
text.push("Channel: lbry://" + channelName);
}
else
*/
console.log(value);
if (value['author']) {
text.push('author: ' + value['author']);
}
if (value['description']) {
text.push(value['description']);
}
// if (value['content_type'])
// {
// text.push("*Content Type:* " + value['content_type']);
// }
if (value['nsfw']) {
text.push('*Warning: Adult Content*');
}
//"fee":{"currency":"LBC","amount":186,"version":"_0_0_1","address":"bTGoFCakvQXvBrJg1b7FJzombFUu6iRJsk"}
if (value['fee']) {
const fees = [];
text.push('Price: ' + value['fee'].amount + ' *' + value['fee'].currency + '*');
}
if (!claim['is controlling']) {
// the following is based on https://lbry.io/faq/claimtrie-implementation
const lastTakeoverHeight = claimsForName['nLastTakeoverHeight'],
maxDelay = 4032, // 7 days of blocks at 2.5min per block
activationDelay = Math.min(maxDelay, Math.floor((claimBlockHeight - lastTakeoverHeight) / 32)),
takeoverHeight = claimBlockHeight + activationDelay,
secondsPerBlock = 161, // in theory this should be 150, but in practice its closer to 161
takeoverTime = Date.now() + (takeoverHeight - currentHeight) * secondsPerBlock * 1000;
text.push('Takes effect on approx. **' + moment(takeoverTime, 'x').format('MMMM Do [at] HH:mm [UTC]') + '** (block ' + takeoverHeight + ')');
}
const richEmbeded = {
author: {
name: value['author'] || 'Anonymous',
url: `http://open.lbry.io/${claim['name']}#${claim['claimId']}`,
icon_url: 'http://barkpost-assets.s3.amazonaws.com/wp-content/uploads/2013/11/3dDoge.gif'
},
title: 'lbry://' + (channelName ? channelName + '/' : '') + claim['name'],
color: 1399626,
description: escapeSlackHtml(text.join('\n')),
footer: {
text: 'Block ' + claimBlockHeight + ' • Claim ID ' + claim['claimId']
},
image: { url: !value['nsfw'] ? value['thumbnail'] || '' : '' },
url: 'http://open.lbry.io/' + claim['name']
};
discordPost(text, richEmbeded);
}
});
} catch (e) {
console.error(e);
}
});
}
function escapeSlackHtml(txt) {
return txt
.replace('&', '&amp;')
.replace('<', '&lt;')
.replace('>', '&gt;');
}
function getClaimsForTxid(txid) {
return lbryCall('getclaimsfortx', txid).catch(function(err) {
// an error here most likely means the transaction is spent,
// which also means there are no claims worth looking at
return [];
});
}
function getLastBlock() {
return new Promise(function(resolve, reject) {
mongo.collection('claimbot').findOne({}, function(err, obj) {
if (err) {
reject(err);
} else if (!obj) {
mongo.collection('claimbot').createIndex({ last_block: 1 }, { unique: true });
resolve(null);
} else {
resolve(obj.last_block);
}
});
});
}
function setLastBlock(block) {
return new Promise(function(resolve, reject) {
mongo.collection('claimbot').findOneAndUpdate({ last_block: { $exists: true } }, { last_block: block }, { upsert: true, returnOriginal: false }, function(err, obj) {
if (!err && obj && obj.value.last_block != block) {
reject('Last value should be ' + block + ', but it is ' + obj.value.last_block);
} else {
resolve();
}
});
});
}
function discordPost(text, params) {
let richEmbeded = new Discord.RichEmbed(params);
channels.forEach(channel => {
discordBot.channels
.get(channel)
.send('', richEmbeded)
.catch(console.error);
});
}
function lbryCall(...args) {
return new Promise(function(resolve, reject) {
lbry.cmd(...args, function(err, ...response) {
if (err) {
reject(new Error('JSONRPC call failed. Args: [' + args.join(', ') + ']'));
} else {
resolve(...response);
}
});
});
}

View file

@ -1,51 +0,0 @@
'use strict';
let commands = require('../../config/commands');
const Discord = require('discord.js');
let initialized = false;
let discordBot = null;
let commandsList = null;
module.exports = {
init: init
};
function init(discordBot_) {
if (initialized) {
throw new Error('init was already called once');
}
discordBot = discordBot_;
discordBot.on('message', checkForCommand);
}
/**
*
* @param {String} message
*/
let checkForCommand = function(message) {
//build the command list ONLY on first run
let firstRun = false;
if (commandsList === null) {
firstRun = true;
commandsList = '';
}
//for each message go through all the commands and check if there are any matches
Object.keys(commands).forEach(command => {
//during the first run also build the cache
if (firstRun) commandsList += command + ', ';
//if a command is found
if (!message.author.bot && message.content.toLowerCase().indexOf(command.toLowerCase()) >= 0 && commands[command].operation === 'send') {
//send a message to the channel according to the config
message.channel.send('', new Discord.RichEmbed(commands[command].bundle));
}
});
//if the user is requesting the list of commands, then print it
if (!message.author.bot && message.content.toLowerCase().indexOf('!helpcommands') >= 0) {
let bundle = commands['!helpcommands'].bundle;
commandsList = commandsList.replace(/,\s$/g, '');
bundle.description = '**' + commandsList + '**';
message.channel.send('', new Discord.RichEmbed(bundle));
}
};

View file

@ -1,24 +0,0 @@
/*
// Example #1 Command(use this to make your Commands with triggers like !demo)
exports.commands = [
"demo" // command name that will be used for next lines of code below
]
exports.demo = {
usage: "<subcommand>", //command usage like !demo <@username>, exclude !demo
description: 'description of command', //the description of command for !help command
process: function(bot,msg,suffix){
// Here the bot,msg and suffix is available, this function can be async if needed.
}
}
// Example #2 Function(use this to make your Functions that dont need trigger words unlike !demo)
exports.custom = [
"myFunction" //change this to your function name
]
exports.myFunction = function(bot) {
// Other functions that need to be ran once on bootup!
// For example a timed function and or some init stuff..
}
*/

View file

@ -1,209 +0,0 @@
let needle = require('needle');
let config = require('config');
let hasHashBotChannels = require('../helpers.js').hasHashBotChannels;
let inPrivate = require('../helpers.js').inPrivate;
let ChannelID = config.get('hashbot').mainchannel;
exports.commands = [
'hash' // command that is in this file, every command needs it own export as shown below
];
exports.custom = ['timedhash'];
exports.timedhash = function(bot) {
setInterval(function() {
sendMiningInfo(bot);
}, 6 * 60 * 60 * 1000);
function sendMiningInfo(bot) {
needle.get('https://explorer.lbry.io/api/v1/status', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('Explorer API is not available');
} else {
let data = response.body;
let height = Number(data.status.height);
let hashrate = data.status.hashrate;
let difficulty = Number(data.status.difficulty);
needle.get('https://whattomine.com/coins/164.json', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('whattomine API is not available');
}
let data = response.body;
let reward = Number(data.block_reward);
let block_time = Number(data.block_time);
let difficulty24 = Number(data.difficulty24);
let description =
'Hashrate: ' +
numberWithCommas(hashrate) +
'\n' +
'Difficulty: ' +
numberWithCommas(difficulty.toFixed(0)) +
'\n' +
'Difficulty 24 Hour Average: ' +
numberWithCommas(difficulty24.toFixed(0)) +
'\n' +
'Current block: ' +
numberWithCommas(height.toFixed(0)) +
'\n' +
'Block Time: ' +
numberWithCommas(block_time.toFixed(0)) +
' seconds \n' +
'Block Reward: ' +
numberWithCommas(reward.toFixed(0)) +
' LBC \n' +
'Sources: https://explorer.lbry.io & \n' +
'https://whattomine.com/coins/164-lbc-lbry';
const embed = {
description: description,
color: 7976557,
author: {
name: 'LBRY Network Stats',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
};
bot.channels.get(ChannelID).send({ embed });
return;
});
}
});
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
};
exports.hash = {
usage: '',
description: 'Displays current Hashrate of Network\n**!hash power <Mh/s>**\n Displays potential Earnings For Given Hashrate',
process: function(bot, msg, suffix) {
let command = '!hash';
let words = suffix
.trim()
.split(' ')
.filter(function(n) {
return n !== '';
});
let profitcommand = words[0];
let myhashrate = words[1];
if (profitcommand == 'power') {
sendProfitInfo(bot, msg, suffix);
return;
} else {
sendMiningInfo(bot, msg, suffix);
return;
}
function sendMiningInfo(bot, msg, suffix) {
if (!inPrivate(msg) && !hasHashBotChannels(msg)) {
msg.channel.send('Please use <#' + ChannelID + '> or DMs to talk to hash bot.');
return;
}
needle.get('https://explorer.lbry.io/api/v1/status', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('Explorer API is not available');
} else {
let data = response.body;
let height = Number(data.status.height);
let hashrate = data.status.hashrate;
let difficulty = Number(data.status.difficulty);
needle.get('https://whattomine.com/coins/164.json', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('whattomine API is not available');
}
let data = response.body;
let reward = Number(data.block_reward);
let block_time = Number(data.block_time);
let difficulty24 = Number(data.difficulty24);
let description =
'Hashrate: ' +
numberWithCommas(hashrate) +
'\n' +
'Difficulty: ' +
numberWithCommas(difficulty.toFixed(0)) +
'\n' +
'Difficulty 24 Hour Average: ' +
numberWithCommas(difficulty24.toFixed(0)) +
'\n' +
'Current block: ' +
numberWithCommas(height.toFixed(0)) +
'\n' +
'Block Time: ' +
numberWithCommas(block_time.toFixed(0)) +
' seconds \n' +
'Block Reward: ' +
numberWithCommas(reward.toFixed(0)) +
' LBC \n' +
'Sources: https://explorer.lbry.io & \n' +
'https://whattomine.com/coins/164-lbc-lbry';
const embed = {
description: description,
color: 7976557,
author: {
name: 'LBRY Network Stats',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
};
msg.channel.send({ embed });
return;
});
}
});
}
function sendProfitInfo(bot, msg, suffix) {
needle.get('https://whattomine.com/coins/164.json', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('whattomine API is not available');
} else {
words = suffix
.trim()
.split(' ')
.filter(function(n) {
return n !== '';
});
let myhashrate = words[1];
if (myhashrate == '' || myhashrate == null || myhashrate == undefined || myhashrate == ' ') {
myhashrate = '100';
}
let Diff = response.body.difficulty24;
let Reward = response.body.block_reward;
let myHash = Number(myhashrate);
let LBC = myHash / 2000 * (1 / ((Diff * 2) ^ 32) * Reward) * 3600;
let LBC24 = myHash / 2000 * (1 / ((Diff * 2) ^ 32) * Reward) * 86400;
let LBC1w = myHash / 2000 * (1 / ((Diff * 2) ^ 32) * Reward) * 604800;
let LBC1m = myHash / 2000 * (1 / ((Diff * 2) ^ 32) * Reward) * 2628000;
let message =
'With **' +
myHash +
' Mh/s** and Average 24 hour Difficulty: **' +
Diff.toFixed(0) +
'**\n' +
'You can potentially earn the following amounts of **LBC**: \n' +
'1 Hour = **' +
LBC.toFixed(4) +
'** \n' +
'1 Day = **' +
LBC24.toFixed(2) +
'** \n' +
'1 Week = **' +
LBC1w.toFixed(4) +
'** \n' +
'1 Month = **' +
LBC1m.toFixed(4) +
'** \n';
const embed = {
description: message,
color: 7976557,
author: {
name: 'Hashing Power Calculator!',
icon_url: 'https://i.imgur.com/nKHVQgq.png'
}
};
msg.channel.send({ embed });
return;
}
});
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
};

View file

@ -1,8 +0,0 @@
const discordIRC = require('elmadev-discord-irc').default;
const config = require('config');
const ircconfig = config.get('irc');
exports.custom = ['irc'];
exports.irc = function(bot) {
discordIRC([ircconfig]);
};

View file

@ -1,44 +0,0 @@
let inPrivate = require('../helpers.js').inPrivate;
let { RichEmbed } = require('discord.js');
exports.custom = [
'lbrylink' //change this to your function name
];
exports.lbrylink = async function(bot, msg, suffix) {
bot.on('message', msg => {
if (inPrivate(msg)) {
return;
}
if (msg.content.includes('lbry://')) {
try {
// Extract the URL(s).
const urls = msg.content
.replace(new RegExp("(lbry:\\/\\/)", "g"), "https://open.lbry.io/")
.match(/\bhttps?:\/\/\S+/gi)
.filter(url => url !== "https://open.lbry.io/");
const cleanURLs = [];
for (const i in urls) {
// Check if Username Was Supplied
const user = urls[i].match("<@.*>");
if (user) {
const { username } = msg.mentions.users.get(user[0].slice(2, -1));
urls[i] = urls[i].replace(user[0], `@${username}`);
}
// Push clean URLs to the array.
cleanURLs.push(urls[i]);
}
if (cleanURLs.length < 1) return;
const linkEmbed = new RichEmbed();
linkEmbed
.setAuthor("LBRY Linker")
.setDescription("I see you tried to post a LBRY URL, here's a friendly hyperlink to share and for others to access your content with a single click:")
.setColor(7976557);
cleanURLs.forEach(url => linkEmbed.addField("Open with LBRY:", url, true));
return msg.channel.send({embed: linkEmbed});
} catch (e) {
console.log(e);
msg.channel.send("Something went wrong when trying to run the lbrylinker, contact a moderator.");
}
}
});
};

View file

@ -1,15 +0,0 @@
/*'use strict';
// Moderation module, handles banning and kicking and more...
let hasPerms = require('../helpers.js').hasPerms;
exports.commands = [
"m"
]
exports.m = {
usage: "<subcommand> <reason>",
description: 'ban: bans the user with the reason\n kick: get adress for your deposits\n',
process: function(bot,msg,suffix){
console.log(msg.member.user);
}
}*/

View file

@ -1,650 +0,0 @@
'use strict';
let jp = require('jsonpath');
let moment = require('moment');
let numeral = require('numeral');
let request = require('request');
let config = require('config');
let hasPriceBotChannels = require('../helpers.js').hasPriceBotChannels;
let inPrivate = require('../helpers.js').inPrivate;
let ChannelID = config.get('pricebot').mainchannel;
exports.commands = ['price'];
exports.price = {
usage: '<currency> <amount>',
description: 'displays price of lbc',
process: function(bot, msg, suffix) {
var options = {
defaultCurrency: 'BTC',
// supported currencies and api steps to arrive at the final value
currencies: {
USD: {
steps: ['LBCBTC', 'BTCUSD'],
format: '$0,0.00',
sign: 'USD '
},
GBP: {
steps: ['LBCBTC', 'BTCGBP'],
format: '£0,0.00',
sign: '£'
},
AUD: {
steps: ['LBCBTC', 'BTCAUD'],
format: '$0,0.00',
sign: 'AUD '
},
BRL: {
steps: ['LBCBTC', 'BTCBRL'],
format: 'R$0,0.00',
sign: 'R$'
},
CAD: {
steps: ['LBCBTC', 'BTCCAD'],
format: '$0,0.00',
sign: 'CAD '
},
CHF: {
steps: ['LBCBTC', 'BTCCHF'],
format: 'CHF 0,0.00',
sign: 'CHF'
},
CLP: {
steps: ['LBCBTC', 'BTCCLP'],
format: '$0,0.00',
sign: 'CLP '
},
CNY: {
steps: ['LBCBTC', 'BTCCNY'],
format: '¥0,0.00',
sign: '¥'
},
DKK: {
steps: ['LBCBTC', 'BTCDKK'],
format: 'kr 0,0.00',
sign: 'kr'
},
EUR: {
steps: ['LBCBTC', 'BTCEUR'],
format: '€0,0.00',
sign: '€'
},
HKD: {
steps: ['LBCBTC', 'BTCHKD'],
format: '$0,0.00',
sign: 'HKD '
},
INR: {
steps: ['LBCBTC', 'BTCINR'],
format: '₹0,0.00',
sign: '₹'
},
ISK: {
steps: ['LBCBTC', 'BTCISK'],
format: 'kr 0,0.00',
sign: 'kr'
},
JPY: {
steps: ['LBCBTC', 'BTCJPY'],
format: '¥0,0.00',
sign: '¥'
},
KRW: {
steps: ['LBCBTC', 'BTCKRW'],
format: '₩0,0.00',
sign: '₩'
},
NZD: {
steps: ['LBCBTC', 'BTCNZD'],
format: '$0,0.00',
sign: 'NZD '
},
PLN: {
steps: ['LBCBTC', 'BTCPLN'],
format: 'zł 0,0.00',
sign: 'zł'
},
RUB: {
steps: ['LBCBTC', 'BTCRUB'],
format: 'RUB 0,0.00',
sign: 'RUB'
},
SEK: {
steps: ['LBCBTC', 'BTCSEK'],
format: 'kr 0,0.00',
sign: 'kr'
},
SGD: {
steps: ['LBCBTC', 'BTCSGD'],
format: '$0,0.00',
sign: 'SGD '
},
THB: {
steps: ['LBCBTC', 'BTCTHB'],
format: '฿0,0.00',
sign: '฿'
},
TWD: {
steps: ['LBCBTC', 'BTCTWD'],
format: 'NT$0,0.00',
sign: 'NT$'
},
MYR: {
steps: ['LBCBTC', 'BTCMYR'],
format: 'RM0,0.00',
sign: 'RM'
},
IDR: {
steps: ['LBCBTC', 'BTCIDR'],
format: 'Rp0,0.00',
sign: 'Rp'
},
VND: {
steps: ['LBCBTC', 'BTCVND'],
format: '₫ 0,0.00',
sign: '₫'
},
PHP: {
steps: ['LBCBTC', 'BTCPHP'],
format: '₱ 0,0.00',
sign: '₱'
},
BND: {
steps: ['LBCBTC', 'BTCBND'],
format: 'B$ 0,0.00',
sign: 'B$'
},
SAR: {
steps: ['LBCBTC', 'BTCSAR'],
format: 'SR0,0.00',
sign: 'SR'
},
MXN: {
steps: ['LBCBTC', 'BTCMXN'],
format: 'Mex$ 0,0.00',
sign: 'Mex$'
},
TRY: {
steps: ['LBCBTC', 'BTCTRY'],
format: '₺ 0,0.00',
sign: '₺'
},
MMK: {
steps: ['LBCBTC', 'BTCMMK'],
format: 'K 0,0.00',
sign: 'K'
},
KHR: {
steps: ['LBCBTC', 'BTCKHR'],
format: '៛ 0,0.00',
sign: '៛'
},
AED: {
steps: ['LBCBTC', 'BTCAED'],
format: 'د.إ0,0.00',
sign: 'د.إ'
},
ZAR: {
steps: ['LBCBTC', 'BTCZAR'],
format: 'R 0,0.00',
sign: 'R'
},
PGK: {
steps: ['LBCBTC', 'BTCPGK'],
format: 'K 0,0.00',
sign: 'K'
},
EGP: {
steps: ['LBCBTC', 'BTCEGP'],
format: 'EGP 0,0.00',
sign: 'EGP'
},
NOK: {
steps: ['LBCBTC', 'BTCNOK'],
format: 'kr0,0.00',
sign: 'kr'
},
HUF: {
steps: ['LBCBTC', 'BTCHUF'],
format: 'ft0,0.00',
sign: 'ft'
},
ALL: {
steps: ['LBCBTC', 'BTCALL'],
format: 'L 0,0.00',
sign: 'L'
},
GEL: {
steps: ['LBCBTC', 'BTCGEL'],
format: 'GEL 0,0.00',
sign: 'GEL'
},
MDL: {
steps: ['LBCBTC', 'BTCMDL'],
format: 'lei 0,0.00',
sign: 'lei'
},
BAM: {
steps: ['LBCBTC', 'BTCBAM'],
format: 'KM 0,0.00',
sign: 'KM'
},
BAM: {
steps: ['LBCBTC', 'BTCBAM'],
format: 'KM 0,0.00',
sign: 'KM'
},
AZN: {
steps: ['LBCBTC', 'BTCAZN'],
format: '₼ 0,0.00',
sign: '₼'
},
AMD: {
steps: ['LBCBTC', 'BTCAMD'],
format: '֏ 0,0.00',
sign: '֏'
},
BYN: {
steps: ['LBCBTC', 'BTCBYN'],
format: 'Br 0,0.00',
sign: 'Br'
},
BTN: {
steps: ['LBCBTC', 'BTCBTN'],
format: 'Nu 0,0.00',
sign: 'Nu'
},
NPR: {
steps: ['LBCBTC', 'BTCNPR'],
format: 'रु 0,0.00',
sign: 'रु'
},
BDT: {
steps: ['LBCBTC', 'BTCBDT'],
format: '৳ 0,0.00',
sign: '৳'
},
PKR: {
steps: ['LBCBTC', 'BTCPKR'],
format: 'Rs 0,0.00',
sign: 'Rs'
},
ARS: {
steps: ['LBCBTC', 'BTCARS'],
format: '$ 0,0.00',
sign: '$'
},
PLN: {
steps: ['LBCBTC', 'BTCPLN'],
format: 'zł 0,0.00',
sign: 'zł'
},
HRK: {
steps: ['LBCBTC', 'BTCHRK'],
format: 'kn 0,0.00',
sign: 'kn'
},
BTC: {
steps: ['LBCBTC'],
format: '0,0[.][00000000] BTC',
sign: 'BTC'
}
},
// api steps
api: {
LBCBTC: {
url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LBC',
path: '$.result.Bid'
},
BTCUSD: {
url: 'https://blockchain.info/ticker',
path: '$.USD.buy'
},
BTCGBP: {
url: 'https://blockchain.info/ticker',
path: '$.GBP.buy'
},
BTCAUD: {
url: 'https://blockchain.info/ticker',
path: '$.AUD.buy'
},
BTCBRL: {
url: 'https://blockchain.info/ticker',
path: '$.BRL.buy'
},
BTCCAD: {
url: 'https://blockchain.info/ticker',
path: '$.CAD.buy'
},
BTCCHF: {
url: 'https://blockchain.info/ticker',
path: '$.CHF.buy'
},
BTCMYR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=MYR',
path: '$.MYR'
},
BTCCLP: {
url: 'https://blockchain.info/ticker',
path: '$.CLP.buy'
},
BTCCNY: {
url: 'https://blockchain.info/ticker',
path: '$.CNY.buy'
},
BTCDKK: {
url: 'https://blockchain.info/ticker',
path: '$.DKK.buy'
},
BTCEUR: {
url: 'https://blockchain.info/ticker',
path: '$.EUR.buy'
},
BTCHKD: {
url: 'https://blockchain.info/ticker',
path: '$.HKD.buy'
},
BTCINR: {
url: 'https://blockchain.info/ticker',
path: '$.INR.buy'
},
BTCISK: {
url: 'https://blockchain.info/ticker',
path: '$.ISK.buy'
},
BTCJPY: {
url: 'https://blockchain.info/ticker',
path: '$.JPY.buy'
},
BTCKRW: {
url: 'https://blockchain.info/ticker',
path: '$.KRW.buy'
},
BTCNZD: {
url: 'https://blockchain.info/ticker',
path: '$.NZD.buy'
},
BTCPLN: {
url: 'https://blockchain.info/ticker',
path: '$.PLN.buy'
},
BTCRUB: {
url: 'https://blockchain.info/ticker',
path: '$.RUB.buy'
},
BTCSEK: {
url: 'https://blockchain.info/ticker',
path: '$.SEK.buy'
},
BTCSGD: {
url: 'https://blockchain.info/ticker',
path: '$.SGD.buy'
},
BTCTHB: {
url: 'https://blockchain.info/ticker',
path: '$.THB.buy'
},
BTCTWD: {
url: 'https://blockchain.info/ticker',
path: '$.TWD.buy'
},
BTCBND: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=BND',
path: '$.BND'
},
BTCVND: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=VND',
path: '$.VND'
},
BTCPHP: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=PHP',
path: '$.PHP'
},
BTCSAR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=SAR',
path: '$.SAR'
},
BTCMXN: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=MXN',
path: '$.MXN'
},
BTCTRY: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=TRY',
path: '$.TRY'
},
BTCMMK: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=MMK',
path: '$.MMK'
},
BTCKHR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=KHR',
path: '$.KHR'
},
BTCAED: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=AED',
path: '$.AED'
},
BTCZAR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=ZAR',
path: '$.ZAR'
},
BTCPGK: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=PGK',
path: '$.PGK'
},
BTCNOK: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=NOK',
path: '$.NOK'
},
BTCHUF: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=HUF',
path: '$.HUF'
},
BTCALL: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=ALL',
path: '$.ALL'
},
BTCEGP: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=EGP',
path: '$.EGP'
},
BTCGEL: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=GEL',
path: '$.GEL'
},
BTCMDL: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=MDL',
path: '$.MDL'
},
BTCBAM: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=BAM',
path: '$.BAM'
},
BTCKZT: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=KZT',
path: '$.KZT'
},
BTCAZN: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=AZN',
path: '$.AZN'
},
BTCAMD: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=AMD',
path: '$.AMD'
},
BTCBYN: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=BYN',
path: '$.BYN'
},
BTCBTN: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=BTN',
path: '$.BTN'
},
BTCNPR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=NPR',
path: '$.NPR'
},
BTCBDT: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=BDT',
path: '$.BDT'
},
BTCPKR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=PKR',
path: '$.PKR'
},
BTCARS: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=ARS',
path: '$.ARS'
},
BTCPLN: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=PLN',
path: '$.PLN'
},
BTCHRK: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=HRK',
path: '$.HRK'
},
BTCIDR: {
url: 'https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=IDR',
path: '$.IDR'
}
},
// display date/time format
dtFormat: 'Do MMM YYYY h:mma [UTC]',
// refresh rate in milliseconds to retrieve a new price (default to 10 minutes)
refreshTime: 100000
};
var words = suffix
.trim()
.split(' ')
.filter(function(n) {
return n !== '';
});
var currency = words.length > 0 ? words[0].toUpperCase() : options.defaultCurrency;
var amount = words.length > 1 ? parseFloat(words[1], 10) : 1;
var showHelp = isNaN(amount) || Object.keys(options.currencies).indexOf(currency) === -1;
// store the last retrieved rate
var cachedRates = {};
var command = '!price';
var currencies = Object.keys(options.currencies);
for (var i = 0; i < currencies.length; i++) {
cachedRates[currencies[i]] = {
rate: 0,
time: null
};
}
if (showHelp) {
doHelp(bot, msg, suffix);
} else {
if (!hasPriceBotChannels(msg) && !inPrivate(msg)) {
msg.channel.send('Please use <#' + ChannelID + '> or DMs to talk to price bot.');
return;
}
doSteps(bot, currency, amount);
}
function doHelp(bot, msg, suffix) {
if (!hasPriceBotChannels(msg) && !inPrivate(msg)) {
msg.channel.send('Please use <#' + ChannelID + '> or DMs to talk to price bot.');
return;
}
var message =
'**' +
command +
'**: show the price of 1 LBC in ' +
options.defaultCurrency +
'\n' +
'**' +
command +
' help**: this message\n' +
'**' +
command +
' CURRENCY**: show the price of 1 LBC in CURRENCY. Supported values for CURRENCY are Listed Below\n' +
'**' +
command +
' CURRENCY AMOUNT**: show the price of AMOUNT LBC in CURRENCY\n' +
'**Supported Currencies:** *usd*, *gbp*, *eur*, *aud*, *brl*, *cad*, *chf*, *clp*, *cny*, *dkk*, *hkd*, *inr*, *isk*, *jpy*, *krw*, *nzd*, *pln* ,*rub*, *sek*, *sgd*, *thb*, *twd*, *myr*, *bnd*,*vnd*, *php*, *sar*, *mxn*, *try*, *mmk*, *khr*, *aed*, *zar*, *pgk*, *egp*,*nok*, *hrk*, *huf*, *all*, *gel*, *mdl*, *bam* ,*kzt*, *azn*, *amd*, *byn*, *btn*, *npr*, *bdt*, *pkr*, *ars*, *pln*, *idr* and *btc* (case-insensitive)';
msg.channel.send(message);
}
function formatMessage(amount, rate, option) {
var cur = option.sign;
var value = numeral(rate.rate * amount).format(option.format);
return '*' + numeral(amount).format('0,0[.][00000000]') + ' LBC = ' + cur + ' ' + value + '*\n_last updated ' + rate.time.utc().format(options.dtFormat) + '_';
}
function doSteps(bot, currency, amount) {
var option = options.currencies[currency];
var shouldReload = true;
if (cachedRates[currency]) {
var cache = cachedRates[currency];
shouldReload = cache.time === null || moment().diff(cache.time) >= options.refreshTime;
if (!shouldReload) {
var message = formatMessage(amount, cache, option);
msg.channel.send(message);
}
}
if (shouldReload) {
// copy the steps array
var steps = [];
for (var i = 0; i < option.steps.length; i++) {
steps.push(option.steps[i]);
}
processSteps(bot, currency, 0, amount, steps, option);
}
}
function processSteps(bot, currency, rate, amount, steps, option) {
if (steps.length > 0) {
var pairName = steps[0];
if (!options.api[pairName]) {
msg.channel.send('There was a configuration error. ' + pairName + ' pair was not found.');
return;
}
var pair = options.api[pairName];
request.get(pair.url, function(error, response, body) {
if (error) {
msg.channel.send(err.message ? err.message : 'The request could not be completed at this time. Please try again later.');
return;
}
var pairRate = 0;
try {
pairRate = jp.query(JSON.parse(body), pair.path);
if (Array.isArray(pairRate) && pairRate.length > 0) {
pairRate = pairRate[0];
}
} catch (ignored) {
// invalid response or pair rate
}
if (pairRate > 0) {
rate = rate === 0 ? pairRate : rate * pairRate;
steps.shift();
if (steps.length > 0) {
processSteps(bot, currency, rate, amount, steps, option);
return;
}
// final step, cache and then response
var result = {
rate: rate,
time: moment()
};
cachedRates[currency] = result;
msg.channel.send(formatMessage(amount, result, option));
} else {
msg.channel.send('The rate returned for the ' + pairName + ' pair was invalid.');
}
});
}
}
}
};

View file

@ -1,43 +0,0 @@
let hasPerms = require('../helpers.js').hasPerms;
let inPrivate = require('../helpers.js').inPrivate;
exports.commands = [
'purge' // command that is in this file, every command needs it own export as shown below
];
exports.purge = {
usage: '<number of messages>',
description: 'Deletes Messages',
process: function(bot, msg, suffix) {
if (inPrivate(msg)) {
msg.channel.send("You Cant Purge Message In DM's!");
return;
}
if (hasPerms(msg)) {
if (!suffix) {
var newamount = '2';
} else {
var amount = Number(suffix);
var adding = 1;
var newamount = amount + adding;
}
let messagecount = newamount.toString();
msg.channel
.fetchMessages({
limit: messagecount
})
.then(messages => {
msg.channel.bulkDelete(messages);
// Logging the number of messages deleted on both the channel and console.
msg.channel.send('Deletion of messages successful. \n Total messages deleted including command: ' + newamount).then(message => message.delete(5000));
console.log('Deletion of messages successful. \n Total messages deleted including command: ' + newamount);
})
.catch(err => {
console.log('Error while doing Bulk Delete');
console.log(err);
});
} else {
msg.channel.send('only moderators can use this command!').then(message => message.delete(5000));
}
}
};

View file

@ -1,171 +0,0 @@
let request = require('request');
let config = require('config');
let hasPerms = require('../helpers.js').hasPerms;
let inPrivate = require('../helpers.js').inPrivate;
let ChannelID = config.get('gitrelease').channel;
exports.commands = [
'releasenotes' // command that is in this file, every command needs it own export as shown below
];
exports.releasenotes = {
usage: '',
description: 'gets current release notes from GITHUB',
process: function(bot, msg, suffix) {
const headers = {
'Content-Type': 'application/json',
'User-Agent': 'Super Agent/0.0.1'
};
// Configure the request
const options = {
url: 'https://api.github.com/repos/lbryio/lbry-desktop/releases/latest',
method: 'GET',
headers: headers
};
// Start the request
let message;
request(options, function(error, response, body) {
let releasemessage = JSON.parse(body).body;
let releasename = JSON.parse(body).name;
let releasedate = JSON.parse(body).published_at;
let releaseurl = JSON.parse(body).html_url;
if (releasemessage.length < 2000) {
message = {
embed: {
title: '*Download ' + releasename + ' here!*',
description: releasemessage,
url: releaseurl,
color: 7976557,
timestamp: releasedate,
author: {
name: 'LBRY Desktop release Notes for ' + releasename,
icon_url: 'http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png'
},
footer: {
icon_url: 'https://i.imgur.com/yWf5USu.png',
text: 'LBRY Desktop Updated '
}
}
};
if (inPrivate(msg)) {
msg.channel.send(message);
return;
}
if (hasPerms(msg) && suffix === 'post') {
bot.channels.get(ChannelID).send(message);
} else {
msg.channel.send(msg.author + ' Release notes sent via DM');
msg.author.send(message);
}
} else {
message = releasemessage
.trim()
.split('###')
.filter(function(n) {
return n !== '';
});
let releasemessage1 = message[0];
let releasemessage2 = message[1];
let releasemessage3 = message[2];
let releasemessage4 = message[3];
let releasemessage5 = message[4];
let message1 = {
embed: {
title: '*Download ' + releasename + ' here!*',
description: releasemessage1,
url: releaseurl,
color: 7976557,
timestamp: releasedate,
author: {
name: 'LBRY Desktop Release Notes for ' + releasename,
icon_url: 'http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png'
},
footer: {
icon_url: 'https://i.imgur.com/yWf5USu.png',
text: 'LBRY Desktop Updated '
}
}
};
let message2 = {
embed: {
description: releasemessage2,
color: 7976557,
timestamp: releasedate,
author: {
icon_url: 'http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png'
},
footer: {
icon_url: 'https://i.imgur.com/yWf5USu.png',
text: 'LBRY Desktop Updated '
}
}
};
let message3 = {
embed: {
description: releasemessage3,
color: 7976557,
timestamp: releasedate,
author: {
icon_url: 'http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png'
},
footer: {
icon_url: 'https://i.imgur.com/yWf5USu.png',
text: 'LBRY Desktop Updated '
}
}
};
let message4 = {
embed: {
description: releasemessage4,
color: 7976557,
timestamp: releasedate,
author: {
icon_url: 'http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png'
},
footer: {
icon_url: 'https://i.imgur.com/yWf5USu.png',
text: 'LBRY Desktop Updated '
}
}
};
let message5 = {
embed: {
description: releasemessage5,
color: 7976557,
timestamp: releasedate,
author: {
icon_url: 'http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png'
},
footer: {
icon_url: 'https://i.imgur.com/yWf5USu.png',
text: 'LBRY Desktop Updated '
}
}
};
if (inPrivate(msg)) {
msg.channel.send(message1);
msg.channel.send(message2);
msg.channel.send(message3);
msg.channel.send(message4);
msg.channel.send(message5);
return;
}
if (hasPerms(msg) && suffix === 'post') {
bot.channels.get(ChannelID).send(message1);
bot.channels.get(ChannelID).send(message2);
bot.channels.get(ChannelID).send(message3);
bot.channels.get(ChannelID).send(message4);
bot.channels.get(ChannelID).send(message5);
} else {
msg.channel.send(msg.author + ' Release notes sent via DM');
msg.author.send(message1);
msg.author.send(message2);
msg.author.send(message3);
msg.author.send(message4);
msg.author.send(message5);
}
}
});
}
};

View file

@ -1,21 +0,0 @@
/*exports.commands = [
"remind" // command that is in this file, every command needs it own export as shown below
]
exports.custom = [
"checkReminders"
]
exports.remind = {
usage: "<time> <message>",
description: 'description of command',
process: async function(bot,msg,suffix){
console.log(suffix);
// Here the bot,msg and suffix is avaible, this function can be async if needed.
}
}
exports.checkReminders = function(bot) {
// Other functions that needs to be ran once on bootup!
// For example a timed function and or some init stuff..
}*/

View file

@ -1,127 +0,0 @@
let config = require('config');
let botconfig = config.get('bot');
let rolelist = config.get('rolelist');
exports.commands = [
'addrole', // command that is in this file, every command needs it own export as shown below
'delrole',
'roles'
];
exports.addrole = {
usage: '<role>',
description: 'Adds you to specified role',
process: function(bot, msg, suffix) {
// Here the bot,msg and suffix is avaible, this function can be async if needed.
let newrole = msg.guild.roles.find('name', suffix);
let baserole = msg.guild.roles.find('name', rolelist.baserole);
// Checks if the user put a role in the message.
if (suffix) {
// Checks if the role mentioned in the message is in the allowed roles listed in the wunderbot config.
if (rolelist.allowedroles.includes(suffix)) {
// Checks if the role even exists in the discord server
if (newrole !== null) {
// Checks if the member has the role that they are trying to add
if (!msg.member.roles.find('name', suffix)) {
msg.member.addRole(newrole).then(msg.channel.send(msg.member + ' has been added to the ' + suffix + ' role!'));
if (rolelist.baserole !== ' ') {
if (baserole !== null) {
if (!msg.member.roles.find('name', rolelist.baserole)) {
msg.member.addRole(baserole).then(msg.channel.send(msg.member + ' has been added to the ' + rolelist.baserole + ' role!'));
}
} else {
msg.channel.send('The ' + rolelist.baserole + " Role doesn't exist. Please add that role first!");
}
}
} else {
msg.channel.send('It seems that you already have that role! Try removing it first with the ' + botconfig.prefix + 'delrole command!');
}
} else {
msg.channel.send('The role ' + '`' + suffix + '`' + ' does not exist!');
}
} else {
msg.channel.send("That role isn't one you can add yourself to! Please run the " + botconfig.prefix + 'roles command to find out which ones are allowed.');
}
} else {
msg.channel.send('Please specify a role. Type ' + botconfig.prefix + 'roles to see which you may add!');
}
}
};
exports.delrole = {
usage: '<role>',
description: 'Deletes the specified role from your account',
process: function(bot, msg, suffix) {
// Here in the bot, msg and suffix are available, this function can be async if needed.
let oldrole = msg.guild.roles.find('name', suffix);
// Checks if the user put a role in the message.
if (suffix) {
// Checks if the role mentioned in the message is in the allowed roles listed in the Wunderbot config.
if (rolelist.allowedroles.includes(suffix)) {
// Checks if the role exists in the Discord server
if (oldrole !== null) {
// Checks if the member has the role that they are trying to add
if (msg.member.roles.find('name', suffix)) {
msg.member.removeRole(oldrole).then(msg.channel.send(msg.member + ' has been removed from the ' + suffix + ' role!'));
} else {
msg.channel.send("You don't seem to have that role! Try adding it first with the " + botconfig.prefix + 'addrole command!');
}
} else {
msg.channel.send('The role ' + '`' + suffix + '`' + ' does not exist!');
}
} else {
msg.channel.send("That role isn't one you can add yourself to! Please run the " + botconfig.prefix + 'roles command to find out which ones are allowed.');
}
} else {
msg.channel.send('Please specify a role. Type ' + botconfig.prefix + 'roles to see which you may add!');
}
}
};
exports.roles = {
usage: '',
description: 'displays roles you can give yourself',
process: function(bot, msg, suffix) {
// Here in the bot, msg and suffix are available, this function can be async if needed.
msg.channel.send({
embed: {
color: 3447003,
title: 'Wunderbot',
description: 'You have accessed the rolebot function of Wunderbot!',
fields: [
{
name: 'List of roles',
value: buildRoleString(rolelist.allowedroles),
inline: false
},
{
name: 'How to add a role to yourself',
value: '!addrole (role) - Adds a specified role to yourself.\n!addrole International would add the International role.',
inline: false
},
{
name: 'How to remove a role from yourself',
value: '!delrole (role) - Removed a specified role from yourself.\n!delrole International would remove the International role.',
inline: false
},
{
name: 'NOTE',
value: 'The above roles are case sensitive.',
inline: false
}
],
footer: {
icon_url: msg.author.avatarURL,
text: 'Requested by: ' + JSON.stringify(msg.author.username)
}
}
});
//msg.channel.send(JSON.stringify(rolelist.allowedroles));
}
};
function buildRoleString(roles) {
let str = '';
for (let i = 0; i < roles.length; i++) {
str += '`' + roles[i] + '`' + '\n';
}
return str;
}

View file

@ -1,124 +0,0 @@
const authors = [];
let warned = [];
let banned = [];
let messagelog = [];
let config = require('config');
let botlog = config.get('moderation').logchannel;
let hasPerms = require('../helpers.js').hasPerms;
let inPrivate = require('../helpers.js').inPrivate;
let hasExcludedSpamChannels = require('../helpers.js').hasExcludedSpamChannels;
let hasExcludedSpamUsers = require('../helpers.js').hasExcludedSpamUsers;
/**
* Add simple spam protection to your discord server.
* @param {Bot} bot - The discord.js CLient/bot
* @param {object} options - Optional (Custom configuarion options)
* @return {[type]} [description]
*/
exports.custom = ['antiSpam'];
exports.antiSpam = function(bot) {
const warnBuffer = 5;
const maxBuffer = 10;
const interval = 1500;
const warningMessage = ', Stop spamming or you will be banned! This is your warning!';
const banMessage = 'has been banned for spamming!';
const maxDuplicatesWarning = 5;
const maxDuplicatesBan = 10;
bot.on('message', msg => {
if (inPrivate(msg) || msg.author.bot || hasPerms(msg) || hasExcludedSpamChannels(msg) || hasExcludedSpamUsers(msg)) {
return;
}
if (msg.author.id != bot.user.id) {
let now = Math.floor(Date.now());
authors.push({
time: now,
author: msg.author.id
});
messagelog.push({
message: msg.content,
author: msg.author.id
});
// Check how many times the same message has been sent.
let msgMatch = 0;
for (let i = 0; i < messagelog.length; i++) {
if (messagelog[i].message == msg.content && messagelog[i].author == msg.author.id && msg.author.id !== bot.user.id) {
msgMatch++;
}
}
// Check matched count
if (msgMatch == maxDuplicatesWarning && !warned.includes(msg.author.id)) {
warn(msg, msg.author.id);
}
if (msgMatch == maxDuplicatesBan && !banned.includes(msg.author.id)) {
ban(msg, msg.author.id);
}
let matched = 0;
for (let i = 0; i < authors.length; i++) {
if (authors[i].time > now - interval) {
matched++;
if (matched == warnBuffer && !warned.includes(msg.author.id)) {
warn(msg, msg.author.id);
} else if (matched == maxBuffer) {
if (!banned.includes(msg.author.id)) {
ban(msg, msg.author.id);
}
}
} else if (authors[i].time < now - interval) {
authors.splice(i);
warned.splice(warned.indexOf(authors[i]));
banned.splice(warned.indexOf(authors[i]));
}
if (messagelog.length >= 200) {
messagelog.shift();
}
}
}
});
/**
* Warn a user
* @param {Object} msg
* @param {string} userid userid
*/
function warn(msg, userid) {
warned.push(msg.author.id);
msg.channel.send(msg.author + ' ' + warningMessage);
}
/**
* Ban a user by the user id
* @param {Object} msg
* @param {string} userid userid
* @return {boolean} True or False
*/
function ban(msg, userid) {
for (let i = 0; i < messagelog.length; i++) {
if (messagelog[i].author == msg.author.id) {
messagelog.splice(i);
}
}
banned.push(msg.author.id);
let user = msg.channel.guild.members.find(member => member.user.id === msg.author.id);
if (user) {
user
.ban()
.then(member => {
msg.channel.send(msg.author + ' ' + banMessage);
bot.channels.get(botlog).send(msg.author + ' ' + banMessage);
return true;
})
.catch(() => {
msg.channel.send('insufficient permission to kick ' + msg.author + ' for spamming.');
return false;
});
}
}
};

View file

@ -1,248 +0,0 @@
let request = require('request');
let wget = require('wget');
let fs = require('fs');
let config = require('config');
let hasSpeechBotChannels = require('../helpers.js').hasSpeechBotChannels;
let inPrivate = require('../helpers.js').inPrivate;
let ChannelID = config.get('speechbot').mainchannel;
//debug output "true/false" outputs same error as slack message in console if set to true
//if set to false console will be left blank like normal
//some have more info on file details of error
let FullDebug = 'true';
//outputs response from speech, very bulk reply
let ResponseDebug = 'false';
exports.commands = [
'speech' // command that is in this file, every command needs it own export as shown below
];
exports.speech = {
usage: '<name>',
description: 'gets top claim from spee.ch, coming soon post to spee.ch',
process: function(bot, msg, suffix) {
if (!hasSpeechBotChannels(msg) && !inPrivate(msg)) {
msg.channel.send('Please use <#' + ChannelID + '> or DMs to talk to speech bot.');
return;
}
let command = '!speech';
words = suffix
.trim()
.split(' ')
.filter(function(n) {
return n !== '';
});
let imagename = words[0];
//check if image name is help, if it is then do help message
if (imagename == 'help') {
doHelp(bot, msg, suffix);
return;
} else {
//check if imagename is defined if not do error
if (imagename === undefined) {
if (FullDebug === 'true') {
let message = '`no name provided`';
console.log('no name provided');
msg.channel.send(message);
doHelp(bot, msg, suffix);
return;
} else {
let message = '`no name provided`';
msg.channel.send(message);
doHelp(bot, msg, suffix);
return;
}
}
//set second word to url
let filepath = words[1];
//check if a url is provided if none do help message
if (filepath === undefined) {
if (FullDebug === 'true') {
let message = '`no url provided, fetching image from:`\n' + 'https://spee.ch/' + imagename;
console.log('no url provided');
msg.channel.send(message);
return;
} else {
let message = '`no url provided, fetching image from:`\n' + 'https://spee.ch/' + imagename;
msg.channel.send(message);
return;
}
}
//prepare url for other uses
//we will just set filepath to url to be safe
let url = filepath;
//parse first 4 letters of url should be http
let linkvalid = url.slice(0, 4);
//check of url provided begins with http in not throw error and help message
if (linkvalid !== 'http') {
if (FullDebug === 'true') {
let message = '`error not a valid url, please start with http or https`';
console.log('invalid url provided: ' + filepath);
msg.channel.send(message);
return;
} else {
let message = '`error not a valid url, please start with http or https`';
msg.channel.send(message);
return;
}
}
//function to check if url is an image
let isUriImage = function(uri) {
//make sure we remove any nasty GET params
uri = uri.split('?')[0];
//moving on, split the uri into parts that had dots before them
let parts = uri.split('.');
//get the last part ( should be the extension )
let extension = parts[parts.length - 1];
//define some image types to test against
let imageTypes = ['jpg', 'jpeg', 'tiff', 'png', 'gif', 'bmp'];
//check if the extension matches anything in the list. if it does set true if not set false
if (imageTypes.indexOf(extension) !== -1) {
return true;
} else {
return false;
}
};
//check if url is an image if its not throw error and help message
if (isUriImage(url) === false) {
if (FullDebug === 'true') {
let message = '`error not a valid image url, be sure the link includes a file type`';
console.log('invalid url provided: ' + url);
msg.channel.send(message);
return;
} else {
let message = '`error not a valid image url, be sure the link includes a file type`';
msg.channel.send(message);
return;
}
}
//set third word to nsfw, with it being an optional functionality
let eighteen = words[2];
//check is NSFW if yes or no sets proper value if none
if (eighteen == '' || eighteen == 'none' || eighteen == undefined || eighteen == null || eighteen == 'no' || eighteen == 'false' || eighteen == false || eighteen == 'n') {
eighteen = 'no';
} else {
eighteen = 'yes';
}
//prepare url for wget
let source = url;
//parse the filename to use to save file
filepath = source.split('/').pop();
//set proper directory for downloading image
let outputFile = 'speech-uploads/' + filepath;
//set download directory to current working directory
let dir = process.cwd();
//set full path to directory for speech uploading
let fullpath = dir + '\\speech-uploads\\' + filepath;
//download url via wget
let download = wget.download(url, outputFile);
//check if url is reachable if not throw error
download.on('error', function(err) {
if (FullDebug === 'true') {
console.log('error could not reach: ' + url + ' : ' + err);
let message = '`error url could not be reached`';
msg.channel.send(message);
return;
} else {
let message = '`error url could not be reached`';
msg.channel.send(message);
return;
}
});
download.on('end', output => {
//if no errors and file ready -> do the request
output && doSteps(bot, imagename, url, eighteen);
});
}
//send help message
function doHelp(bot, msg, suffix) {
msg.channel.send({
embed: {
title: '',
description:
'**!speech `<Name>`** : *displays top claim on speech* \n\n\n' +
'**COMING SOON POSTING TO SPEECH** \n\n' +
'**!speech `<Name> <URL> <NSFW>`** : *Uploads Image URL to Spee.ch* \n' +
'**NOTE : dont include spaces in name (NSFW is optional true/false, if left blank will default to false)** \n' +
'EXAMPLE : `!speech my-image-name https://url/to/image.png false`',
color: 7976557,
author: {
name: 'Speech Bot Help',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
});
}
//send post request to speech
function doSteps(bot, imagename, url, eighteen) {
request.post(
//url to send post request
'https://spee.ch/api/publish',
//json payload
{
json: {
name: imagename,
file: fullpath,
nsfw: eighteen
}
},
//get response from server
function(error, response, body) {
//output response if ResponseDebug set to true
if (ResponseDebug === 'true') {
console.log(response);
console.log(error);
console.log(body.success);
console.log(body.message);
}
//check speech response for file path error, if found throw internal error!
if (body.message === 'no files found in request') {
if (FullDebug === 'true') {
console.log('no file found: ' + fullpath);
let message = '`Failed to upload file internally!!`\n please contact <@244245498746241025> or another moderator if the issue persists';
msg.channel.send(message);
return;
} else {
let message = '`Failed to upload file internally!!`\n please contact <@244245498746241025> or another moderator if the issue persists';
msg.channel.send(message);
return;
}
}
//check speech response for filename error, if found throw internal error!
if (body.message === 'no name field found in request') {
if (FullDebug === 'true') {
console.log('no name field found: ' + imagename);
let message = '`Failed to upload file internally!!`\n please contact <@244245498746241025> or another moderator if the issue persists';
msg.channel.send(message);
return;
} else {
let message = '`Failed to upload file internally!!`\n please contact <@244245498746241025> or another moderator if the issue persists';
msg.channel.send(message);
return;
}
}
//if no errors post this message
let message = 'uploading... \n "name":"' + imagename + '",\n "URL": "' + url + '",\n "nsfw":"' + eighteen + '"\n to spee.ch';
console.log('uploading... \n "name":"' + imagename + '",\n "file name": "' + filepath + '",\n "url":"' + url + '"\n "path":"' + fullpath + '"\n "nsfw": "' + eighteen + '"');
msg.channel.send(message);
}
);
}
}
};

View file

@ -1,160 +0,0 @@
let jp = require('jsonpath');
let moment = require('moment');
let numeral = require('numeral');
let request = require('request');
let config = require('config');
let needle = require('needle');
let hasStatsBotChannels = require('../helpers.js').hasStatsBotChannels;
let inPrivate = require('../helpers.js').inPrivate;
let ChannelID = config.get('statsbot').mainchannel;
let statsurl = 'https://coinmarketcap.com/currencies/library-credit/';
exports.commands = [
'stats' // command that is in this file, every command needs it own export as shown below
];
exports.stats = {
usage: '',
description: 'Displays list of current Market stats',
process: function(bot, msg) {
needle.get('https://api.coinmarketcap.com/v2/ticker/1298/?convert=BTC', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('coinmarketcap API is not available');
} else {
let data = response.body.data;
let rank = data.rank;
let price_usd = Number(data.quotes.USD.price);
let price_btc = Number(data.quotes.BTC.price);
let market_cap_usd = Number(data.quotes.USD.market_cap);
let circulating_supply = Number(data.circulating_supply);
let total_supply = Number(data.total_supply);
let percent_change_1h = Number(data.quotes.USD.percent_change_1h);
let percent_change_24h = Number(data.quotes.USD.percent_change_24h);
let json = response.body[0];
let newjson = parse_obj(json);
let parse = JSON.stringify(newjson);
let volume24_usd = Number(data.quotes.USD.volume_24h);
let dt = new Date();
let timestamp = dt.toUTCString();
let hr_indicator = ':thumbsup:';
let day_indicator = ':thumbsup:';
if (percent_change_1h < 0) {
hr_indicator = ':thumbsdown:';
}
if (percent_change_24h < 0) {
day_indicator = ':thumbsdown:';
}
needle.get('https://api.coinmarketcap.com/v2/ticker/1298/?convert=GBP', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('coinmarketcap API is not available');
} else {
data = response.body.data;
let price_gbp = Number(data.quotes.GBP.price);
needle.get('https://api.coinmarketcap.com/v2/ticker/1298/?convert=EUR', function(error, response) {
if (error || response.statusCode !== 200) {
msg.channel.send('coinmarketcap API is not available');
} else {
data = response.body.data;
let price_eur = Number(data.quotes.EUR.price);
let description =
'**Rank: [' +
rank +
'](' +
statsurl +
')**\n' +
'**Data**\n' +
'Market Cap: [$' +
numberWithCommas(market_cap_usd) +
'](' +
statsurl +
') \n' +
'Total Supply: [' +
numberWithCommas(total_supply) +
' LBC](' +
statsurl +
')\n' +
'Circulating Supply: [' +
numberWithCommas(circulating_supply) +
' LBC](' +
statsurl +
')\n' +
'24 Hour Volume: [$' +
volume24_usd +
'](' +
statsurl +
') \n\n' +
'**Price**\n' +
'BTC: [₿' +
price_btc.toFixed(8) +
'](' +
statsurl +
')\n' +
'USD: [$' +
price_usd.toFixed(2) +
'](' +
statsurl +
') \n' +
'EUR: [€' +
price_eur.toFixed(2) +
'](' +
statsurl +
') \n' +
'GBP: [£' +
price_gbp.toFixed(2) +
'](' +
statsurl +
') \n\n' +
'**% Change**\n' +
'1 Hour: [' +
percent_change_1h +
'](' +
statsurl +
') ' +
hr_indicator +
' \n\n' +
'1 Day: [' +
percent_change_24h +
'](' +
statsurl +
') ' +
day_indicator +
' \n\n';
const embed = {
description: description,
color: 7976557,
footer: {
text: 'Last Updated: ' + timestamp
},
author: {
name: 'Coin Market Cap Stats (LBC)',
url: statsurl,
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
};
msg.channel.send({ embed });
}
});
}
});
}
});
function parse_obj(obj) {
let array = [];
let prop;
for (prop in obj) {
if (obj.hasOwnProperty(prop)) {
let key = parseInt(prop, 10);
let value = obj[prop];
if (typeof value == 'object') {
value = parse_obj(value);
}
array[key] = value;
}
}
return array;
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
};

View file

@ -1,35 +0,0 @@
'use strict';
//let config = require("config");
//let rolelist = config.get("rolelist");
const Discord = require('discord.js');
let initialized = false;
let discordBot = null;
module.exports = {
init: init
};
function init(discordBot_) {
if (initialized) {
throw new Error('init was already called once');
}
discordBot = discordBot_;
discordBot.on('message', checkForCommand);
}
/**
*
* @param {String} message
*/
let checkForCommand = function(message) {
//if the close command is found
if (!message.author.bot && message.content.toLowerCase().indexOf('!close') >= 0) {
//send the -close command twice with a 4 seconds timeout
message.channel.send('-close').catch(console.error);
setTimeout(() => {
message.channel.send('-close').catch(console.error);
}, 4000);
}
};

View file

@ -1,9 +0,0 @@
'use strict';
exports.commands = ['tip'];
exports.tip = {
usage: '<subcommand>',
description: 'balance: get your balance\n deposit: get address for your deposits\n withdraw ADDRESS AMOUNT: withdraw AMOUNT credits to ADDRESS\n <user> <amount>: mention a user with @ and then the amount to tip them',
process: function(bot) {
return; // Tipping is now handled by the separate tipbot(in branch tipbot_dc), no need to to anything here...
}
};

View file

@ -1,276 +0,0 @@
let hasPerms = require('../helpers.js').hasPerms;
let inPrivate = require('../helpers.js').inPrivate;
exports.custom = ['onUserJoin'];
exports.onUserJoin = function(bot) {
bot.on('guildMemberAdd', member => {
member
.send({
embed: {
title: '*Click here for more info about LBRY!*',
description:
'**Welcome to LBRY Discord Community, you are now officially a LBRYian!** \n' +
'If you are new to LBRY and would like to learn more, see the links at the end of this message. \n' +
'This community allows LBRYians to interact with the team directly and for us to engage users in order to grow the LBRY platform! \n' +
'**Looking for *Rewards Verification*? Please make a request in the #verification channel by typing **-new** - this will create a ticket (channel) for your request. A mod will reach out to you, please be patient . **Note: DO NOT message any team members or post in other channels about verification concerns.**. Only 1 Reward account is allowed per household** \n',
url: 'https://lbry.io/what',
color: 7976557,
author: {
name: 'Welcome to LBRY Discord Community',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
bot.channels
.get('369896313082478594')
.send(
member +
', Please enable Direct Messages from server members to communicate fully with our bot, it is located in the user setting area under Privacy & Safety tab, select the option allow direct messages from server members\nSince the bot could not send you our Welcome message please check the posts in <#431211007050776577> and see the Wunderbot and tipbot commands by typing `!help` in <#369896313082478594>'
);
});
member
.send({
embed: {
description:
'1. Be respectful to other community members. Harrasment and vulgarity will not be tolerated \n' +
'2. Do not spam, advertise or post referral links \n' +
'3. Use appropriate channels for your discussions/questions. If you are looking for help with LBRY, use #help, for price talk, use #market-and-trading \n' +
'4. #general discussions should be at least somewhat related to LBRY, otherwise there is #random \n' +
'5. Do not post **not safe for work (NFSW)** content in any non-marked channels, there is #random-nsfw for that \n' +
'6. Do not direct message and LBRY team or mods without being asked to do so \n' +
'7. Do not request free LBC, begging will not be tolerated \n',
color: 7976557,
author: {
name: 'Ground rules',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
member
.send({
embed: {
description:
'1. Type !tip help in the #bot-sandbox channel to interact with our Tipbot which can be used to send and receive LBRY Credits (LBC). **Enable 2FA in your Discord account settings!** \n' +
'2. See the Frequently Asked Questions (FAQ) section below prior to asking for help or information on LBRY \n' +
'3. Backing up your LBRY wallet is your responsbility, see FAQ link below \n' +
'4. You can find the LBRY Block explorer at https://explorer.lbry.io \n' +
'5. Want to contribute more? Check out https://lbry.io/faq/contributing \n' +
'6. Are you a dev? Check out the #dev channel \n' +
'7. Want to share something you published? Post it on the #publishers channel \n',
color: 7976557,
author: {
name: 'Helpful hints',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
member
.send({
embed: {
title: '*Click here for more info about LBRY!*',
description:
'[**LBRY**](https://lbry.io) is a protocol providing fully decentralized network for the discovery, distribution, and payment of data. It utilizes the [**LBRY blockchain**](https://lbry.io/what#the-network) as a global namespace and database of digital content. Blockchain entries contain searchable content metadata, identities, and rights and access rules. \n[_**Download the LBRY App here**_](https://lbry.io/get)',
url: 'https://lbry.io/what',
color: 7976557,
author: {
name: 'What is LBRY?',
url: 'https://lbry.io/what',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
member
.send({
embed: {
title: '*Click here to see all LBRY Frequently Asked Questions (FAQ)!*',
description:
'Want to backup your LBRY wallet? [**Backup**](https://lbry.io/faq/how-to-backup-wallet) \nLooking for LBRY data? [**Behind the scenes files**](https://lbry.io/faq/lbry-directories) \nTrouble starting LBRY? [**Startup troubleshooting**](https://lbry.io/faq/startup-troubleshooting) \nNeed help finding your log files (will help us troubleshoot!)? [**Find logs**](https://lbry.io/faq/how-to-find-lbry-log-file) \nNot able to stream any content? [**Troublshoot streaming**](https://lbry.io/faq/unable-to-stream)\nNeed help with publishing? [**How to Publish**](https://lbry.io/faq/how-to-publish) \nWant more LBRY Credits (LBC)? [**Get LBC**](https://lbry.io/faq/earn-credits) \nLooking for referral information? [**Referrals**](https://lbry.io/faq/referrals)',
url: 'https://lbry.io/faq',
color: 7976557,
author: {
name: 'LBRY FAQ',
url: 'https://lbry.io/faq',
icon_url: 'https://spee.ch/8/Id5Qoc3w.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
member
.send({
embed: {
title: '*Have you checked out spee.ch yet?!*',
description:
"[**spee.ch**](https://spee.ch) runs on top of the LBRY network - it's essentially an open source, censorship resistent and decentralized image and video sharing site with the added benefit of being a web-based (works on mobile too!) gateway into the LBRY network. spee.ch can be used to retrieve LBRY images/videos that are free by accessing them through a web browser. \nFor example, if content is located at lbry://loose-cannons-episode1#12c87bb42dd8832167b1e54edf72bbd37bc47622, you can view it on spee.ch at: https://spee.ch/12c87bb42dd8832167b1e54edf72bbd37bc47622/loose-cannons-episode1. You can also view channels on spee.ch, such as: https://spee.ch/@copchronicles:5c039dc7423657e59d78939df72c186e43273675 or https://spee.ch/@MinutePhysics:589276465a23c589801d874f484cc39f307d7ec7 \n\nspee.ch also allows you to create a channel to group your uploads and retreive them easily. These channels are separate from any you may have in the LBRY app since they exist on the spee.ch site via a login process. You can even share your channel name and password so that others can contribute to it.",
url: 'https://spee.ch/about',
color: 7976557,
author: {
name: 'spee.ch',
url: 'https://spee.ch',
icon_url: 'http://www.pd4pic.com/images/flag-green-blue-purple-indigo-bars-background.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
});
};
exports.commands = [
'welcome' // command that is in this file, every command needs it own export as shown below
];
exports.welcome = {
usage: '<@username>',
description: 'send welcome message to specified user',
process: function(bot, msg, suffix) {
if (inPrivate(msg)) {
msg.channel.send('command cannot be used in a DM');
return;
}
if (suffix == '') {
msg.channel.send('no user defined');
return;
}
if (!hasPerms(msg)) {
msg.channel.send('You Dont Have Permission To Use This Command!');
return;
}
msg.mentions.members
.first()
.send({
embed: {
title: '*Click here for more info about LBRY!*',
description:
'**Welcome to LBRY Discord Community, you are now officially a LBRYian!** \n' +
'If you are new to LBRY and would like to learn more, see the links at the end of this message. \n' +
'This community allows LBRYians to interact with the team directly and for us to engage users in order to grow the LBRY platform! \n' +
'**Looking for *Rewards Verification*? Please make a request in the #verification channel by typing **-new** - this will create a ticket (channel) for your request. A mod will reach out to you, please be patient, **Note: DO NOT message any team members or post in other channels about verification concerns.**. Only 1 Reward account is allowed per household** \n',
url: 'https://lbry.io/what',
color: 7976557,
author: {
name: 'Welcome to LBRY Discord Community',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
msg.channel.send(
msg.mentions.members.first() +
', Please enable Direct Messages from server members to communicate fully with our bot, it is located in the user setting area under Privacy & Safety tab, select the option allow direct messages from server members'
);
});
msg.mentions.members
.first()
.send({
embed: {
description:
'1. Be respectful to other community members. Harrasment and vulgarity will not be tolerated \n' +
'2. Do not spam, advertise or post referral links \n' +
'3. Use appropriate channels for your discussions/questions. If you are looking for help with LBRY, use #help, for price talk, use #market-and-trading \n' +
'4. #general discussions should be at least somewhat related to LBRY, otherwise there is #random \n' +
'5. Do not post **not safe for work (NFSW)** content in any non-marked channels, there is #random-nsfw for that \n' +
'6. Do not direct message and LBRY team or mods without being asked to do so \n' +
'7. Do not request free LBC, begging will not be tolerated \n',
color: 7976557,
author: {
name: 'Ground rules',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
msg.mentions.members
.first()
.send({
embed: {
description:
'1. Type !tip help in the #bot-sandbox channel to interact with our Tipbot which can be used to send and receive LBRY Credits (LBC). **Enable 2FA in your Discord account settings!** \n' +
'2. See the Frequently Asked Questions (FAQ) section below prior to asking for help or information on LBRY \n' +
'3. Backing up your LBRY wallet is your responsbility, see FAQ link below \n' +
'4. You can find the LBRY Block explorer at https://explorer.lbry.io \n' +
'5. Want to contribute more? Check out https://lbry.io/faq/contributing \n' +
'6. Are you a dev? Check out the #dev channel \n' +
'7. Want to share something you published? Post it on the #publishers channel \n',
color: 7976557,
author: {
name: 'Helpful hints',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
msg.mentions.members
.first()
.send({
embed: {
title: '*Click here for more info about LBRY!*',
description:
'[**LBRY**](https://lbry.io) is a protocol providing fully decentralized network for the discovery, distribution, and payment of data. It utilizes the [**LBRY blockchain**](https://lbry.io/what#the-network) as a global namespace and database of digital content. Blockchain entries contain searchable content metadata, identities, and rights and access rules. \n[_**Download the LBRY App here**_](https://lbry.io/get)',
url: 'https://lbry.io/what',
color: 7976557,
author: {
name: 'What is LBRY?',
url: 'https://lbry.io/what',
icon_url: 'https://i.imgur.com/yWf5USu.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
msg.mentions.members
.first()
.send({
embed: {
title: '*Click here to see all LBRY Frequently Asked Questions (FAQ)!*',
description:
'Want to backup your LBRY wallet? [**Backup**](https://lbry.io/faq/how-to-backup-wallet) \nLooking for LBRY data? [**Behind the scenes files**](https://lbry.io/faq/lbry-directories) \nTrouble starting LBRY? [**Startup troubleshooting**](https://lbry.io/faq/startup-troubleshooting) \nNeed help finding your log files (will help us troubleshoot!)? [**Find logs**](https://lbry.io/faq/how-to-find-lbry-log-file) \nNot able to stream any content? [**Troublshoot streaming**](https://lbry.io/faq/unable-to-stream)\nNeed help with publishing? [**How to Publish**](https://lbry.io/faq/how-to-publish) \nWant more LBRY Credits (LBC)? [**Get LBC**](https://lbry.io/faq/earn-credits) \nLooking for referral information? [**Referrals**](https://lbry.io/faq/referrals)',
url: 'https://lbry.io/faq',
color: 7976557,
author: {
name: 'LBRY FAQ',
url: 'https://lbry.io/faq',
icon_url: 'https://spee.ch/8/Id5Qoc3w.png'
}
}
})
.catch(console.error)
.then(console.log('could not send dm'));
msg.mentions.members
.first()
.send({
embed: {
title: '*Have you checked out spee.ch yet?!*',
description:
"[**spee.ch**](https://spee.ch) runs on top of the LBRY network - it's essentially an open source, censorship resistent and decentralized image and video sharing site with the added benefit of being a web-based (works on mobile too!) gateway into the LBRY network. spee.ch can be used to retrieve LBRY images/videos that are free by accessing them through a web browser. \nFor example, if content is located at lbry://loose-cannons-episode1#12c87bb42dd8832167b1e54edf72bbd37bc47622, you can view it on spee.ch at: https://spee.ch/12c87bb42dd8832167b1e54edf72bbd37bc47622/loose-cannons-episode1. You can also view channels on spee.ch, such as: https://spee.ch/@copchronicles:5c039dc7423657e59d78939df72c186e43273675 or https://spee.ch/@MinutePhysics:589276465a23c589801d874f484cc39f307d7ec7 \n\nspee.ch also allows you to create a channel to group your uploads and retreive them easily. These channels are separate from any you may have in the LBRY app since they exist on the spee.ch site via a login process. You can even share your channel name and password so that others can contribute to it.",
url: 'https://spee.ch/about',
color: 7976557,
author: {
name: 'spee.ch',
url: 'https://spee.ch',
icon_url: 'http://www.pd4pic.com/images/flag-green-blue-purple-indigo-bars-background.png'
}
}
})
.catch(function(error) {
console.log('could not send dm');
});
}
};

View file

@ -1,47 +0,0 @@
'use strict';
const fs = require('fs'),
path = require('path');
function getPlugins(srcpath) {
return fs.readdirSync(srcpath);
}
let plugin_directory = path.join(__dirname, 'modules');
let plugins = getPlugins(plugin_directory);
exports.init = function init() {
load_plugins();
};
function load_plugins() {
const dbot = require('./bot.js');
let commandCount = 0;
let otherFunc = 0;
for (let i = 0; i < plugins.length; i++) {
let plugin;
try {
plugin = require(`${plugin_directory}/${plugins[i]}`);
} catch (err) {
console.log(`Improper setup of the '${plugins[i]}' plugin. : ${err}`);
}
if (plugin) {
if ('commands' in plugin) {
for (let j = 0; j < plugin.commands.length; j++) {
if (plugin.commands[j] in plugin) {
dbot.addCommand(plugin.commands[j], plugin[plugin.commands[j]]);
commandCount++;
}
}
}
if ('custom' in plugin) {
for (let j = 0; j < plugin.custom.length; j++) {
if (plugin.custom[j] in plugin) {
dbot.addCustomFunc(plugin[plugin.custom[j]]);
otherFunc++;
}
}
}
}
}
console.log(`Loaded ${dbot.commandCount()} chat commands and ${otherFunc} custom functions.`);
}

View file

@ -1,814 +0,0 @@
{
"!helpcommands": {
"usage": "",
"description": "List of Helpful LBRY Commands",
"operation": "none",
"bundle": {
"url": "",
"title": "",
"description": "filled at runtime",
"color": 7976557,
"author": {
"name": "List of Helpful LBRY Commands",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!what": {
"usage": "",
"description": "What Is LBRY?",
"operation": "send",
"bundle": {
"url": "https://lbry.io/what",
"title": "*Click Here for more Info!*",
"description":
"[**LBRY**](https://lbry.io) is a protocol providing fully decentralized network for the discovery, distribution, and payment of data. It utilizes the [**LBRY blockchain**](https://lbry.io/what#the-network) as a global namespace and database of digital content. Blockchain entries contain searchable content metadata, identities, and rights and access rules. \n[_**Get the app here**_](https://lbry.io/get)",
"color": 7976557,
"author": {
"name": "What Is LBRY?",
"url": "https://lbry.io/what",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!begging": {
"usage": "",
"description": "Don't Request Free Coins Message",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": ":warning: **Please don't request free coins or invites, we have a strict policy against begging. Further offenses will result in removal from the chat.**",
"color": 7976557,
"author": {
"name": "BEGGING!",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!beta": {
"usage": "",
"description": "Beta Message",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Even though LBRY is in Open Beta, it's still beta software! There will be bugs and issues to be worked out. Thanks for your patience!",
"color": 7976557,
"author": {
"name": "Open Beta",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!github": {
"usage": "",
"description": "LBRY GitHub",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "The official GitHub for LBRY is [github.com/lbryio](https://github.com/lbryio)",
"color": 7976557,
"author": {
"name": "LBRY GitHub",
"url": "https://github.com/lbryio",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!appdownload": {
"usage": "",
"description": "LBRY App Installers",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "**Installers for the LBRY app are available for download** [**HERE**](https://lbry.io/get)",
"color": 7976557,
"author": {
"name": "Get The LBRY App",
"url": "http://lbry.io/get?auto=1",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!daemondownload": {
"usage": "",
"description": "LBRY Daemon Installers",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Installers for the LBRY daemon are available for download [**HERE**](https://github.com/lbryio/lbry/releases)",
"color": 7976557,
"author": {
"name": "LBRY Daemon Download",
"url": "https://github.com/lbryio/lbry/releases",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!directories": {
"usage": "",
"description": "LBRY App Directories",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "You can find details about the folders your LBRY files are stored in at [lbry.io/faq/lbry-directories](https://lbry.io/faq/lbry-directories)",
"color": 7976557,
"author": {
"name": "LBRY Directories",
"url": "https://lbry.io/faq/lbry-directories",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!faq": {
"usage": "",
"description": "LBRY FAQ",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "These questions and many more have been answered on the [FAQ Page](https://lbry.io/faq/)",
"color": 7976557,
"author": {
"name": "LBRY FAQ",
"url": "https://lbry.io/faq/",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!contribute": {
"usage": "",
"description": "Contributing to LBRY",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Want to help develop and contribute to the LBRY Project?\nCheck out **[GUIDE](https://lbry.io/faq/contributing)** to help you get you started.",
"color": 7976557,
"author": {
"name": "Contributing to LBRY",
"url": "https://lbry.io/faq/contributing",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!news": {
"usage": "",
"description": "LBRY Inc. Newsfeed",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Check out the newsfeed **[HERE](https://lbry.io/news)**.",
"color": 7976557,
"author": {
"name": "LBRY Inc. Newsfeed",
"url": "https://lbry.io/news",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!name": {
"usage": "",
"description": "Change Name Message",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
":warning: Hey, glad to see you love **LBRY** so much, but for the safety of our users we ask that you avoid using **Discord names** that include the word **LBRY**, names of **LBRY staff**, names of other **LBRY members** on the this server or any name that could be considered **deceptive**. This is to prevent impersonation and scams.",
"color": 7976557,
"author": {
"name": "Discord Username",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!mining": {
"usage": "",
"description": "Mining LBRY Credits (LBC)",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "We have a dedicated channel for mining discussion, feel free to join <#363049669636390913>. We have guides pinned in that channel to get you started mining some LBC!",
"color": 7976557,
"author": {
"name": "Mining LBC",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!pricestance": {
"usage": "",
"description": "Our Stance on LBC Price",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Details about our stance on price can be found here: [lbry.io/news/acryptypical](https://lbry.io/news/acryptypical)",
"color": 7976557,
"author": {
"name": "CEO's Stance on Price",
"url": "https://lbry.io/news/acryptypical",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!youtuber": {
"usage": "",
"description": "Are You a YouTuber Curious About LBRY?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Are you a YouTuber curious about LBRY? Have a look at [lbry.io/youtube](https://lbry.io/youtube)",
"color": 7976557,
"author": {
"name": "Are You a YouTuber?",
"url": "https://lbry.io/youtube",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!publish": {
"usage": "",
"description": "How to Publish on LBRY?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "We've created a small guide to help with the Publishing features of LBRY, check it out here: [lbry.io/faq/how-to-publish](https://lbry.io/faq/how-to-publish)",
"color": 7976557,
"author": {
"name": "How to Publish on LBRY?",
"url": "https://lbry.io/faq/how-to-publish",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!random": {
"usage": "",
"description": "Off-Topic Messages",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please keep conversations on topic, or move random conversations to #363084227518136322 if you wish to continue",
"color": 7976557,
"author": {
"name": "Random",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!naming": {
"usage": "",
"description": "How Does LBRY Naming Work?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"Names exist so that we can map a human readable and understandable word or term to a more difficult to remember number or ID. In the traditional domain system, names map to numerical IP addresses. In LBRY, names map to a unique, permanent ID representing a piece of digital content and/or a publisher identity. Learn more **[HERE](https://lbry.io/faq/naming)**",
"color": 7976557,
"author": {
"name": "How Does LBRY Naming Work?",
"url": "https://lbry.io/faq/naming",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!bounty": {
"usage": "",
"description": "LBRY Bounties",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"You can earn LBRY Credits for completing tasks as part of the LBRY Bounty Program, All bounties can be seen on the **[Bounty Home Page](https://lbry.io/bounty)**.\nBounties range from simple typo fixes to third party applications and custom projects.",
"color": 7976557,
"author": {
"name": "LBRY Bounties",
"url": "https://lbry.io/bounty",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!roadmap": {
"usage": "",
"description": "The LBRY RoadMap",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"Check out the **[RoadMap](https://lbry.io/roadmap)**, it only outlines past and anticipated technical changes, it does not cover other initiatives. Development was fast and furious among a small group prior to 0.6, and release notes are sparse.",
"color": 7976557,
"author": {
"name": "RoadMap",
"url": "https://lbry.io/roadmap",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!referrals": {
"usage": "",
"description": "What Are Referrals?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see [lbry.io/faq/referrals](https://lbry.io/faq/referrals) - Referral redemptions are currently in test mode and limited to one. But you can see your entire referral history in your LBRY app.",
"color": 7976557,
"author": {
"name": "Referrals",
"url": "https://lbry.io/faq/referrals",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!rewards": {
"usage": "",
"description": "What Are Rewards?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"[Rewards](https://lbry.io/faq/rewards) are given to legitimate users who are using the system (and in turn are testing things for us). In order to redeem rewards, you may need to verify your identity through a credit card or other manual methods.\n Please see [lbry.io/faq/identity-requirements](https://lbry.io/faq/identity-requirements)",
"color": 7976557,
"author": {
"name": "Rewards",
"url": "https://lbry.io/faq/rewards",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!rewardsvsreferrals": {
"usage": "",
"description": "Difference Between Rewards & Referrals?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Rewards are different to referral bonuses. Rewards are given for testing the LBRY software and system. Referrals are given for sharing LBRY with the masses.",
"color": 7976557,
"author": {
"name": "Rewards vs Referrals",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!cc": {
"usage": "",
"description": "Credit Card Verification?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"In an effort to limit abuse, newly invited LBRY users will be required to verify their identity via a credit card or by a manual verification process in order to be eligible for Rewards. Prepaid or virtual credit cards are disallowed. Certain countries (where we've previously seen abuse) are being denied, but that list may expand later on. If you use Tor/Proxy/VPN, you also may be denied. If credit card verification does not work for you, please go to the <#363050496023592961> channel for assistance.\n**Verification is purely optional and ONLY relevant for Rewards. The LBRY app can be used without providing CC information**\n**Please See:https://lbry.io/faq/identity-requirements**",
"color": 7976557,
"author": {
"name": "Credit Card Verification",
"url": "https://lbry.io/faq/identity-requirements",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!verify": {
"usage": "",
"description": "How to Verify?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"Please download the latest version from [HERE](https://lbry.io/get) Upon install, you'll be greeted with a welcome message. If you already had the app installed, please go to the wallet (bank icon in the top right) > Rewards - This should show your current status. New users will need to verify in order to access Rewards. Type !cc or !verification for more information.",
"color": 7976557,
"author": {
"name": "How to Verify?",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!verification": {
"usage": "",
"description": "Verification Help Message",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"If you would like to be verified, please go to <#363050496023592961> and type *`-new`* \nthis will create a ticket (channel) for your request. A LBRY mod will get back to you as soon as possible. We appreciate your patience. Only one account per household is allowed access to LBRY Rewards. Check out our [YouTube Sync FAQ](https://lbry.io/faq/youtube) for assistance with the YouTube Sync rewards verification method.",
"color": 7976557,
"author": {
"name": "Rewards Verification",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!logfile": {
"usage": "",
"description": "How to Find LBRY App Log File?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "You can find your log files by following the guide [HERE](https://lbry.io/faq/how-to-find-lbry-log-file)",
"color": 7976557,
"author": {
"name": "How to Find My LogFile?",
"url": "https://lbry.io/faq/how-to-find-lbry-log-file",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!backup": {
"usage": "",
"description": "How to Backup the Wallet",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see this guide on how to backup your wallet: [lbry.io/faq/how-to-backup-wallet](https://lbry.io/faq/how-to-backup-wallet)",
"color": 7976557,
"author": {
"name": "How to Backup My Wallet?",
"url": "https://lbry.io/faq/how-to-backup-wallet",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!startup": {
"usage": "",
"description": "Startup Troubleshooting?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see [lbry.io/faq/startup-troubleshooting](https://lbry.io/faq/startup-troubleshooting) if you are having trouble getting LBRY to start correctly.",
"color": 7976557,
"author": {
"name": "Startup Troubleshooting",
"url": "https://lbry.io/faq/startup-troubleshooting",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!streamingissues": {
"usage": "",
"description": "Unable to Stream?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see [lbry.io/faq/unable-to-stream](https://lbry.io/faq/unable-to-stream) if you are experiencing problems viewing **ANY** LBRY content.",
"color": 7976557,
"author": {
"name": "Streaming Troubleshooting",
"url": "https://lbry.io/faq/unable-to-stream",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!ports": {
"usage": "",
"description": "LBRY Ports",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"The daemon uses ports **3333** and **4444**. It may interfere with mining software, Start the miner after the app and you should be okay. Also these ports need to be port forwarded on your router. Google is your friend there. \n **Please see this tutorial on how to change ports : [lbry.io/faq/how-to-change-port](https://lbry.io/faq/how-to-change-port)**",
"color": 7976557,
"author": {
"name": "Ports",
"url": "https://lbry.io/faq/how-to-change-port",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!migrate": {
"usage": "",
"description": "How to Migrate Your Wallet/Data",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see [lbry.io/faq/backup-data](https://lbry.io/faq/backup-data) for instructions on how to backup and/or migrate your LBRY data",
"color": 7976557,
"author": {
"name": "How to Backup/Migrate LBRY Data?",
"url": "https://lbry.io/faq/backup-data",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!tipping": {
"usage": "",
"description": "Details About LBRY App Tipping",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see [lbry.io/faq/tipping](https://lbry.io/faq/tipping) for details about tipping in the LBRY app",
"color": 7976557,
"author": {
"name": "LBRY App Tipping?",
"url": "https://lbry.io/faq/tipping",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!email": {
"usage": "",
"description": "How to Change Email in the LBRY App",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "If you need to change your LBRY connected email, please see these instructions [HERE](https://lbry.io/faq/how-to-change-email)",
"color": 7976557,
"author": {
"name": "How to Change Email in the LBRY App",
"url": "https://lbry.io/faq/how-to-change-email",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!cli": {
"usage": "",
"description": "How to Interact with LBRY CLI?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "If you are interested in interacting with the LBRY protocol via commands, check out [lbry.io/faq/how-to-cli](https://lbry.io/faq/how-to-cli)",
"color": 7976557,
"author": {
"name": "Interact with the LBRY CLI",
"url": "https://lbry.io/faq/how-to-cli",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!ipfstorrent": {
"usage": "",
"description": "How Is LBRY Different from IPFS/BitTorrent?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "If you are interested in how LBRY is different from IPFS or BitTorrent, check out [lbry.io/faq/different-ipfs](https://lbry.io/faq/different-ipfs)",
"color": 7976557,
"author": {
"name": "How Is LBRY Different from IPFS/BitTorrent?",
"url": "https://lbry.io/faq/different-ipfs",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!shapeshift": {
"usage": "",
"description": "How Can I Convert My Crypto into LBC?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see this guide on how to convert your crypto into LBC: [lbry.io/faq/shapeshift](https://lbry.io/faq/shapeshift)",
"color": 7976557,
"author": {
"name": "How Can I Convert My Crypto into LBC?",
"url": "https://lbry.io/faq/shapeshift",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!youtube": {
"usage": "",
"description": "What Is YouTube Sync?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see this guide on how YouTube sync works: [lbry.io/faq/youtube](https://lbry.io/faq/youtube)",
"color": 7976557,
"author": {
"name": "What Is YouTube Sync?",
"url": "https://lbry.io/faq/youtube",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!transactions": {
"usage": "",
"description": "What Types of LBRY Transactions Are There?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Please see this guide on [transaction types](https://lbry.io/faq/transaction-types)",
"color": 7976557,
"author": {
"name": "What Types of LBRY Transactions Are There?",
"url": "https://lbry.io/faq/transaction-types",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!team": {
"usage": "",
"description": "Meet the Team!",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Get to know the [Official Team](https://lbry.io/team) behind the scenes of LBRY Inc.",
"color": 7976557,
"author": {
"name": "Meet the Team!",
"url": "https://lbry.io/team",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!phone": {
"usage": "",
"description": "Phone Verification!",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "[Phone number](https://lbry.io/faq/phone) verification provides another method for LBRY users to participate in the Rewards program",
"color": 7976557,
"author": {
"name": "Phone Verification!",
"url": "https://lbry.io/faq/phone",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!basic": {
"usage": "",
"description": "LBRY App Basics!",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "[LBRY App Basics](https://lbry.io/faq/lbry-basics) The purpose of this FAQ is to answer questions about some of the basic functionality available in the LBRY app",
"color": 7976557,
"author": {
"name": "LBRY App Basics!",
"url": "https://lbry.io/faq/lbry-basics",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!shop": {
"usage": "",
"description": "The LBRY Inc. Shop!",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Visit our at-cost [merchandise store](https://shop.lbry.io) to check out the latest LBRY swag.",
"color": 7976557,
"author": {
"name": "The LBRY Inc. Shop!",
"url": "https://shop.lbry.io",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!content": {
"usage": "",
"description": "What Content Can I legally Upload to LBRY?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "[Read our FAQ](https://lbry.io/faq/content) to learn more about LBRY's content policy.",
"color": 7976557,
"author": {
"name": "What Content Can I legally Upload to LBRY?",
"url": "https://lbry.io/faq/content",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!aup": {
"usage": "",
"description": "Acceptable Use Policy",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "[Read Our FAQ](https://lbry.io/faq/acceptable-use-policy) to learn more about LBRY's Acceptable Use Policy",
"color": 7976557,
"author": {
"name": "Acceptable Use Policy",
"url": "https://lbry.io/faq/acceptable-use-policy",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!meet": {
"usage": "",
"description": "Wanna Meet Up?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "LBRY is looking for ambassadors to spread the word to [College campuses](https://lbry.io/college) and [Meetup](https://lbry.io/meet) groups worldwide! ",
"color": 7976557,
"author": {
"name": "Wanna Meet Up?",
"url": "https://lbry.io/meet",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!alpha": {
"usage": "",
"description": "Android Alpha Tester",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "Sign up to become an alpha tester for our [Android app](https://lbry.io/android-alpha)!",
"color": 7976557,
"author": {
"name": "Android Alpha Tester",
"url": "https://lbry.io/android-alpha",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!fund": {
"usage": "",
"description": "Android Alpha Tester",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description": "The [LBRY Fund](https://lbry.fund) receives funding from a portion of the 200m LBC premined credits reserved for Community to spread the use and adoption of the LBRY protocol.",
"color": 7976557,
"author": {
"name": "The LBRY Community Fund",
"url": "https://lbry.fund",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!jiggytom": {
"usage": "",
"description": "What is JiggyTom?",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"[JiggyTom](https://spee.ch/5/jiggytomsuperhero.jpeg) is LBRY's very own superhero. He has special powers which enable him to seek out and fix almost any issue that might occur within planet LBRY. \n\nUse JiggyTom with care. Overuse may result in unforeseen consequences and a meltdown which could lead to thermonuclear LBRYian war. \n\nMake sure you show your appreciation for Jiggy and his team of superheros when they use superpowers to help you. And remember kids, stay safe and behave yourselves",
"color": 7976557,
"author": {
"name": "What is JiggyTom?",
"url": "https://en.wikipedia.org/wiki/Superhero",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
},
"!tipbot": {
"usage": "",
"description": "Tipbot Help Message",
"operation": "send",
"bundle": {
"url": "",
"title": "",
"description":
"Type `!tip help` or `!tips` to interact with our Tipbot which can be used to send and receive LBRY Credits (LBC). <#369896313082478594> should be used to talk to bots in order to avoid spamming other channels. \nRead our [Tipbot FAQ](https://lbry.io/faq/tipbot-discord) on how use the LBRY Discord Tipbot",
"color": 7976557,
"author": {
"name": "Tipbot Help Message",
"url": "",
"icon_url": "https://i.imgur.com/yWf5USu.png"
}
}
}
}

View file

@ -0,0 +1,14 @@
{
"Wunderbot": {
"dbUrl": "mongodb://localhost:27017",
"dbName": "wunderbot",
"token": "**********************************************",
"owner": "132434059270815744",
"defaultPrefix": ".w",
"logLevel": "silly"
},
"Web": {
"clientID": "XXXXX",
"clientSecret": "XXXXX"
}
}

View file

@ -1,101 +0,0 @@
{
// Bot configuration
"bot": {
"token": "discordbottoken",
"prefix": "!",
"debug": false
},
"moderation": {
"perms": ["Moderator Team","LBRY TEAM"], // Roles that have access to all commands.
"logchannel": "371620338263523328" // Channel to log the bots moderation..
},
//define channels for Commands.js usage
"Channels": {
"verification": "363050496023592961",
"mining": "363049669636390913",
"random": "363084227518136322",
"content": "377938982111019010"
},
"speechbot": {
"channels": [
"363089721313722387",
"363085078403874823",
"363086945976320010",
"363086925428555776",
"363086752518242308",
"369896313082478594",
"363087606239461377",
"363088045366312962",
"363084227518136322",
"363084262028607488",
"371620338263523328"
], // Channels speech bot is allowed to post in
"mainchannel": "363085078403874823" // Main Speech Bot channel for directing with help message
},
"pricebot": {
"channels": [
"363050205043621908",
"369896313082478594",
"371620338263523328"
], // Chanels price bot is allowed to post in
"mainchannel": "363050205043621908" // Main Price Bot channel for directing with help message
},
"gitrelease": {
"channel": "370779899650375681" // Channel to send release info to using <!releasenotes post>
},
"spamdetection": {
"channels": [
"363089721313722387",
"363044238486798336",
"363086719391629326",
"363044275363119105",
"363044260938776576",
"372067478542745601",
"369896313082478594",
"371620338263523328"
], // Channels excluded from Spam Detection
"users": [
"244245498746241025"
] // Users to exclude from Spam Detection (include other bots here)
},
"hashbot": {
"channels": [
"363049669636390913",
"369896313082478594",
"371620338263523328"
], // Channels Hash Bot is allowed to post in
"mainchannel": "363049669636390913" // Main Hash Bot channel for directing with help message
},
"statsbot": {
"channels": [
"363050205043621908",
"369896313082478594",
"371620338263523328"
], // Channels Stats Bot is allowed to post in
"mainchannel": "363050205043621908" // Main Stats Bot channel for directing with help message
},
"rolelist": {
// The roles here are Case Sensitive to how they are in your Discord Server!
"baserole": "LBRYian",
"allowedroles": ["NSFW", "Marketers", "Miners", "Off-Topic Chats", "International", "Dev"]
},
"claimbot": {
"channels": [
"377938982111019010"
]
},
"mongodb": {
"url": "mongodb://HOST:PORT/DATABASE"
},
"irc": {
"nickname": "nick",
"server": "chat.freenode.net",
"discordToken": "tokenhere",
"autoSendCommands": [
["PRIVMSG", "NickServ", "IDENTIFY password"]
],
"channelMapping": {
"#shared": "#lbry"
}
}
}

0
logs/.gitkeep Normal file
View file

3102
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,39 +1,51 @@
{
"dependencies": {
"babel-cli": "^6.26.0",
"babel-preset-node8": "^1.2.0",
"bitcoin": "^3.0.1",
"chrono-node": "^1.3.5",
"config": "^1.30.0",
"discord.js": "^11.3.2",
"elmadev-discord-irc": "^2.4.1",
"embed-creator": "^1.2.3",
"jsonpath": "^1.0.0",
"moment": "^2.21.0",
"mongoose": "^4.13.12",
"needle": "^2.0.1",
"node-config": "^0.0.2",
"numeral": "^2.0.6",
"request": "^2.85.0",
"sleep": "^5.1.1",
"wget": "^0.0.1"
},
"scripts": {
"prettier": "prettier --write \"{bot,.}/**/*.{js,json}\" --single-quote --print-width 240",
"build": "babel bot -d dist",
"prod": "babel bot -d dist & node dist/bot.js",
"lint": "prettier --write \"{bot,.}/**/*.{js,json}\" --single-quote --print-width 240",
"precommit": "prettier --write \"{bot,.}/**/*.{js,json}\" --single-quote --print-width 240"
},
"devDependencies": {
"husky": "^0.14.3",
"prettier": "1.8.1"
},
"name": "wunderbot-discord",
"version": "0.0.1",
"description": "LBRYs bot for Discord",
"main": "app.js",
"repository": "https://github.com/filipnyquist/wunderbot-disc",
"author": "filipnyquist <filip@lbry.io>",
"license": "MIT"
"dependencies": {
"commando-provider-mongo": "^1.2.1",
"common-tags": "^1.8.0",
"config": "^1.30.0",
"discord.js": "^11.2.1",
"discord.js-commando": "^0.10.0",
"emoji-regex": "^7.0.0",
"express": "^4.16.3",
"express-session": "^1.15.6",
"moment": "^2.22.2",
"mongodb": "^3.1.0-beta4",
"ms": "^2.1.1",
"passport": "^0.4.0",
"passport-discord": "^0.1.3",
"prettier-eslint-cli": "^4.7.1",
"request": "^2.86.0",
"request-promise-cache": "^2.0.0"
},
"name": "wunderbot",
"version": "0.0.1",
"description": "A discord.js bot, next-gen with a webinterface",
"main": "index.js",
"scripts": {
"build": "babel ./src --experimental --source-maps-inline -d ./dist",
"lint": "eslint ./src",
"precommit": "prettier-eslint --write \"src/**/*.js\" && git add .",
"start": "nodemon ./src/index.js --exec babel-node",
"startna": "babel-node ./src/index.js",
"debug": "nodemon ./src/index.js --inspect-brk=41000 --exec babel-node"
},
"license": "MIT",
"devDependencies": {
"@babel/cli": "7.0.0-beta.44",
"@babel/core": "7.0.0-beta.44",
"@babel/node": "7.0.0-beta.44",
"@babel/preset-env": "7.0.0-beta.44",
"babel-eslint": "^8.1.2",
"eslint": "^4.14.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.8.0",
"husky": "^0.14.3",
"nodemon": "^1.14.8",
"prettier-eslint": "^8.7.0"
},
"repository": "https://github.com/lbryio/lbry-wunderbot",
"author": "Filip Nyquist <filip@lbry.io>",
"contributors": [],
"private": false
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

90
src/bot.js Normal file
View file

@ -0,0 +1,90 @@
import Commando from "discord.js-commando";
import path from "path";
import config from "config";
import { oneLine } from "common-tags";
import { MongoClient } from "mongodb";
import MongoDBProvider from "commando-provider-mongo";
import requestCacheSupport from "request-promise-cache";
import checkCustomCommands from "./helpers/customCommands";
import welcomeEvent from "./helpers/welcomeEvent";
// import controlPanel from "./web/controlPanel";
module.exports = class Wunderbot extends Commando.Client {
constructor() {
super({
owner: config.get("Wunderbot.owner"),
commandPrefix: config.get("Wunderbot.defaultPrefix")
});
// Add our own version of request with cache support :)
this.request = requestCacheSupport;
// ADD our custom commands helper :)
this.on("message", msg => {
checkCustomCommands(msg, this);
});
this.on("guildMemberAdd", member => welcomeEvent(member, this));
this.on("error", console.error)
.on("warn", console.warn)
// .on("debug", console.log)
.on("ready", () => {
console.log(`## ## ## ## ## ## ######## ######## ######## ######## ####### ########
## ## ## ## ## ### ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## #### ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ###### ######## ######## ## ## ##
## ## ## ## ## ## #### ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ### ## ## ## ## ## ## ## ## ## ##
### ### ####### ## ## ######## ######## ## ## ######## ####### ##
`);
console.log(`Client ready; logged in as ${this.user.username}#${this.user.discriminator} (${this.user.id})`);
})
.on("disconnect", () => {
console.warn("Disconnected!");
})
.on("reconnecting", () => {
console.warn("Reconnecting...");
})
.on("commandError", (cmd, err) => {
if (err instanceof Commando.FriendlyError) return;
console.error(`Error in command ${cmd.groupID}:${cmd.memberName}`, err);
})
.on("commandBlocked", (msg, reason) => {
console.log(oneLine`
Command ${msg.command ? `${msg.command.groupID}:${msg.command.memberName}` : ""}
blocked; ${reason}
`);
})
.on("commandPrefixChange", (guild, prefix) => {
console.log(oneLine`
Prefix ${prefix === "" ? "removed" : `changed to ${prefix || "the default"}`}
${guild ? `in guild ${guild.name} (${guild.id})` : "globally"}.
`);
})
.on("commandStatusChange", (guild, command, enabled) => {
console.log(oneLine`
Command ${command.groupID}:${command.memberName}
${enabled ? "enabled" : "disabled"}
${guild ? `in guild ${guild.name} (${guild.id})` : "globally"}.
`);
})
.on("groupStatusChange", (guild, group, enabled) => {
console.log(oneLine`
Group ${group.id}
${enabled ? "enabled" : "disabled"}
${guild ? `in guild ${guild.name} (${guild.id})` : "globally"}.
`);
});
this.setProvider(MongoClient.connect(config.get("Wunderbot.dbUrl"), { useNewUrlParser: true }).then(client => new MongoDBProvider(client.db(config.get("Wunderbot.dbName"))))).catch(console.error);
this.registry
.registerDefaults()
.registerGroup("info", "Info")
.registerGroup("general", "General")
.registerGroup("moderation", "Moderation")
.registerGroup("lbry", "LBRY")
.registerGroup("crypto", "Crypto")
// .registerTypesIn(path.join(__dirname, "types"))
.registerCommandsIn(path.join(__dirname, "commands"));
this.login(config.get("Wunderbot.token"));
// controlPanel(this); // Lets start the control panel!
}
};

View file

@ -0,0 +1,66 @@
import commando from "discord.js-commando";
import Discord from "discord.js";
module.exports = class PriceCommand extends commando.Command {
constructor(client) {
super(client, {
name: "price",
aliases: ["cp", "altprice"],
group: "crypto",
memberName: "price",
description: "Displays price of a specific alt coin from cryptocompare.",
examples: ["price BTC USD 1", "price LBC EUR 100"],
guildOnly: false,
args: [
{
key: "coin",
label: "coin",
prompt: "What coin would you like to check the value of?",
default: "LBC",
type: "string"
},
{
key: "fiat",
label: "fiat",
prompt: "The fiat currency to check?",
default: "USD",
type: "string"
},
{
key: "amount",
label: "amount",
prompt: "The number of coins to check the value of?",
default: 100,
type: "float"
}
]
});
}
async run(msg, { coin, fiat, amount }) {
try {
const url = `https://min-api.cryptocompare.com/data/price?fsym=${coin.toUpperCase()}&tsyms=${fiat.toUpperCase()}`;
const { body, headers } = await this.client.request({
url,
cacheKey: url,
cacheTTL: 120000,
cacheLimit: 3,
resolveWithFullResponse: true
});
const coinVal = JSON.parse(body)[fiat.toUpperCase()];
const embed = new Discord.RichEmbed()
.setTitle(`The value of ${amount} ${coin.toUpperCase()} in ${fiat.toUpperCase()}`)
.setColor(0x00ae86)
.setFooter("Wunderbot | Cryptocompare API", "http://i.imgur.com/w1vhFSR.png")
.setTimestamp(new Date(Date.parse(headers.date)))
.setURL(`https://www.cryptocompare.com/coins/${coin.toLowerCase()}/overview/${fiat.toLowerCase()}`)
.addField("Price per coin", `The price per coin is ${coinVal} ${fiat.toUpperCase()}`)
.addField("Result", `The value of ${amount} ${coin.toUpperCase()} is ${(coinVal * amount).toFixed(3)} ${fiat.toUpperCase()}`);
return msg.embed(embed);
} catch (e) {
console.log(e);
return msg.reply("Could not talk to the cryptocompare API. Try again later.");
}
}
};

View file

@ -0,0 +1,82 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { oneLine } from "common-tags";
// const rawJSON = require('./ranks.json');
module.exports = class RankCommand extends Command {
constructor(client) {
super(client, {
name: "rank",
aliases: ["ranks", "role", "roles", "roleme"],
group: "general",
memberName: "rank",
description: "Adds or removes a public role to a user.",
details: oneLine`Do you want to opt-in to a special channel? Do you want to show what games you play?
This command allows members to get or remove public roles.`,
examples: ["rank give ping"],
args: [
{
key: "action",
label: "action",
prompt: "What action would you like to preform? (`add`, `remove`, or `list`)",
type: "string",
infinite: false
},
{
key: "rank",
label: "rank",
prompt: "",
type: "string",
default: "",
infinite: false
}
],
guildOnly: true,
guarded: true
});
}
async run(msg, args) {
if (args.action.toLowerCase() === "give" || args.action.toLowerCase() === "add") {
if (!msg.guild.member(this.client.user).hasPermission("MANAGE_ROLES")) return msg.reply("I do not have permission to manage roles! Contact a mod or admin.");
const rankToGive = msg.guild.roles.find("name", args.rank);
if (rankToGive === null) return msg.reply("That is not a role! Was your capatalization and spelling correct?");
if (!await msg.guild.settings.get("ranks", null)) return msg.reply(`There are no public roles! Maybe try adding some? Do \`${msg.guild.commandPrefix}rank add role\` to add a role.`);
if (!await msg.guild.settings.get("ranks", []).includes(args.rank)) return msg.reply(`That role can not be added! Use \`${msg.guild.commandPrefix}rank list\` to see a list of ranks you can add.`);
msg.guild
.member(msg.author)
.addRole(msg.guild.roles.find("name", args.rank))
.then(() => {
msg.reply("Rank given.");
})
.catch(() => msg.reply("Something went wrong. Is my role above the role you're trying to give?"));
} else if (args.action.toLowerCase() === "take" || args.action.toLowerCase() === "remove") {
if (!msg.guild.member(this.client.user).hasPermission("MANAGE_ROLES")) return msg.reply("I do not have permission to manage roles! Contact a mod or admin.");
const rankToTake = msg.guild.roles.find("name", args.rank);
if (rankToTake === null) return msg.reply("That is not a role! Was your capatalization and spelling correct?");
if (!await msg.guild.settings.get("ranks", null)) return msg.reply(`There are no public roles! Maybe try adding some? Do \`${msg.guild.commandPrefix}rank add role\` to add a role.`);
if (!await msg.guild.settings.get("ranks", []).includes(args.rank)) return msg.reply(`That role can not be taken! Use \`${msg.guild.commandPrefix}rank list\` to see a list of ranks you can add.`);
msg.guild
.member(msg.author)
.removeRole(msg.guild.roles.find("name", args.rank))
.then(() => {
msg.reply("Rank taken.");
})
.catch(() => msg.reply("Something went wrong. Is my role above the role you're trying to give?"));
} else if (args.action.toLowerCase() === "list") {
const guildRanks = await msg.guild.settings.get("ranks", null);
if (!guildRanks) return msg.reply(`There are no public roles! Maybe try adding some? Do \`${msg.guild.commandPrefix}pubranks add role\` to add a role.`);
let rankList = "";
guildRanks.forEach(rank => {
rankList += `${rank}\n`;
});
const rankEmbed = new RichEmbed();
rankEmbed.setAuthor("Wunderbot | Rank Controller").setColor(7976557);
if (rankList) rankEmbed.addField("Ranks available:", rankList, true);
msg.embed(rankEmbed);
} else {
return msg.reply(`Invalid command usage. Please use \`add\`, \`remove\`, or \`list\`.
**NOTE:** If you're trying to add a public role, do \`${msg.guild.commandPrefix}pubranks add role\`.`);
}
}
};

View file

@ -0,0 +1,64 @@
import moment from "moment";
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { arrayClean, capitalizeFirstLetter, deleteCommandMessages } from "../../components/util.js";
module.exports = class UserInfoCommand extends Command {
constructor(client) {
super(client, {
name: "userinfo",
memberName: "userinfo",
group: "info",
aliases: ["user", "uinfo"],
description: "Gets information about a user.",
format: "MemberID|MemberName(partial or full)",
examples: ["uinfo @Fillerino"],
guildOnly: true,
args: [
{
key: "member",
prompt: "What user would you like to snoop on?",
type: "member"
}
]
});
}
run(msg, { member }) {
const uinfoEmbed = new RichEmbed();
uinfoEmbed
.setAuthor(member.user.tag)
.setThumbnail(member.user.displayAvatarURL)
.setColor(member.displayHexColor)
.addField("ID", member.id, true)
.addField("Name", member.user.username, true)
.addField("Nickname", member.nickname ? member.nickname : "No Nickname", true)
.addField("Status", member.user.presence.status !== "dnd" ? capitalizeFirstLetter(member.user.presence.status) : "Do Not Disturb", true)
.addField(member.user.presence.activity ? capitalizeFirstLetter(member.user.presence.activity.type) : "Activity", member.user.presence.activity ? member.user.presence.activity.name : "Nothing", true)
.addField("Display Color", member.displayHexColor, true)
.addField(
"Role(s)",
member.roles.size > 1
? arrayClean(
null,
member.roles.map(r => {
if (r.name !== "@everyone") {
return r.name;
}
return null;
})
).join(" | ")
: "None",
false
)
.addField("Account created at", moment(member.user.createdAt).format("MMMM Do YYYY [at] HH:mm:ss [UTC]Z"), true)
.addField("Joined server at", moment(member.joinedAt).format("MMMM Do YYYY [at] HH:mm:ss [UTC]Z"), true);
member.roles.size >= 1 ? uinfoEmbed.setFooter(`${member.displayName} has ${member.roles.size - 1} role(s)`) : uinfoEmbed.setFooter(`${member.displayName} has 0 roles`);
deleteCommandMessages(msg, this.client);
return msg.embed(uinfoEmbed);
}
};

View file

@ -0,0 +1,82 @@
// Claimbot is a work in progress, it will use chainquery when done.
/* import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import welcomeEvent from "../../helpers/welcomeEvent";
let bootupTime = null;
module.exports = class ClaimCommand extends Command {
constructor(client) {
super(client, {
name: "claimbot",
aliases: ["cb"],
group: "lbry",
memberName: "claimbot",
description: "Enables/disables the claimbot for the current channel.",
examples: ["claimbot enable"],
args: [
{
key: "action",
label: "action",
prompt: "What action would you like to preform? (`enable` or `disable`)",
type: "string"
}
],
guildOnly: true,
guarded: true
});
client.on("ready", () => {
bootupTime = new Date()
.toISOString()
.slice(0, 19)
.replace("T", " ");
this.checkGuilds();
});
}
hasPermission(msg) {
return this.client.isOwner(msg.author) || msg.member.hasPermission("ADMINISTRATOR");
}
async run(msg, { action }) {
if (action.toLowerCase() === "enable") {
}
if (action.toLowerCase() === "disable") {
}
if (action.toLowerCase() === "test") {
console.log(
await this.getNewClaimsSince(
new Date()
.toISOString()
.slice(0, 19)
.replace("T", " ")
)
);
}
}
checkGuilds(){
}
async getNewClaimsSince(date, guild_id) {
const query = `${`` +
`SELECT ` +
`name, ` +
`value_as_json as value, ` +
`bid_state, ` +
`effective_amount, ` +
`claim_id as claimId ` +
// `,transaction_by_hash_id, ` + // txhash and vout needed to leverage old format for comparison.
// `vout ` +
`FROM claim ` +
`WHERE modified >='`}${date}'`;
const newClaims = await this.client.request({
url: `https://chainquery.lbry.io/api/sql?query=${query}`,
cacheKey: `https://chainquery.lbry.io/api/sql?query=${query}`,
cacheTTL: 1,
cacheLimit: 1,
resolveWithFullResponse: false
});
console.log(newClaims);
}
};
*/

76
src/commands/lbry/hash.js Normal file
View file

@ -0,0 +1,76 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { locked } from "../../components/util.js";
module.exports = class HashCommand extends Command {
constructor(client) {
super(client, {
name: "hash",
aliases: ["hash"],
group: "lbry",
memberName: "lbryhash",
description: "Sends LBRY mining information to the channel.",
throttling: {
usages: 2,
duration: 3
},
guildOnly: true,
args: null
});
}
async run(msg) {
const lockStatus = await locked("hash", msg);
if (lockStatus) {
let channelList = "";
lockStatus.forEach(channel => {
channelList += `<#${channel}> `;
});
return msg.reply(`This command cannot be ran from this channel, only from: ${channelList}`);
}
this.sendMiningInfo(msg);
}
async sendMiningInfo(msg) {
try {
let expResponse = await this.client.request({
url: "https://explorer.lbry.io/api/v1/status",
cacheKey: "https://explorer.lbry.io/api/v1/status",
cacheTTL: 120000,
cacheLimit: 3,
resolveWithFullResponse: true
});
expResponse = JSON.parse(expResponse.body);
let wtmResponse = await this.client.request({
url: "https://whattomine.com/coins/164.json",
cacheKey: "ttps://whattomine.com/coins/164.json",
cacheTTL: 120000,
cacheLimit: 3,
resolveWithFullResponse: true
});
wtmResponse = JSON.parse(wtmResponse.body);
const height = Number(expResponse.status.height);
const { hashrate } = expResponse.status;
const difficulty = Number(expResponse.status.difficulty);
const reward = Number(wtmResponse.block_reward);
const blockTime = Number(wtmResponse.block_time);
const diff24 = Number(wtmResponse.difficulty24);
const hashEmbed = new RichEmbed();
hashEmbed
.setAuthor("LBRY Network Stats", "https://i.imgur.com/yWf5USu.png", "https://explorer.lbry.io")
.setColor(7976557)
.addField("Hashrate", this.numberWithCommas(hashrate), true)
.addField("Difficulty", this.numberWithCommas(difficulty.toFixed(0)), true)
.addField("Difficulty 24 Hour Average:", this.numberWithCommas(diff24.toFixed(0)), true)
.addField("Current block:", this.numberWithCommas(height.toFixed(0)), true)
.addField("Block Time:", this.numberWithCommas(blockTime.toFixed(0)), true)
.addField("Block Reward:", `${reward.toFixed(0)} LBC`, true);
return msg.embed(hashEmbed);
} catch (e) {
console.log(e);
return msg.channel.send("Explorer API is not available");
}
}
numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
};

View file

@ -0,0 +1,60 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { inPrivate } from "../../components/util.js";
module.exports = class LinkCommand extends Command {
constructor(client) {
super(client, {
name: "link",
aliases: ["lbrylink"],
group: "lbry",
memberName: "link",
description: "Responds with a open.lbry.io link when someone posts a LBRY link.",
throttling: {
usages: 2,
duration: 3
},
patterns: [new RegExp("(lbry:\\/\\/)")],
guildOnly: true,
args: [
{
key: "link",
prompt: "Which LBRY link do you want to open?",
type: "string"
}
]
});
}
async run(msg, { link }) {
try {
// Extract the URL(s).
const urls = msg.content
.replace(new RegExp("(lbry:\\/\\/)", "g"), "https://open.lbry.io/")
.match(/\bhttps?:\/\/\S+/gi)
.filter(url => url !== "https://open.lbry.io/");
const cleanURLs = [];
for (const i in urls) {
// Check if Username Was Supplied
const user = urls[i].match("<@.*>");
if (user) {
const { username } = msg.mentions.users.get(user[0].slice(2, -1));
urls[i] = urls[i].replace(user[0], `@${username}`);
}
// Push clean URLs to the array.
cleanURLs.push(urls[i]);
}
if (cleanURLs.length < 1) return;
const linkEmbed = new RichEmbed();
linkEmbed
.setAuthor("LBRY Linker")
.setDescription("I see you tried to post a LBRY URL, here's a friendly hyperlink to share and for others to access your content with a single click:")
.setColor(7976557);
cleanURLs.forEach(url => linkEmbed.addField("Open with LBRY:", url, true));
return msg.embed(linkEmbed);
} catch (e) {
console.log(e);
msg.channel.send("Something went wrong when calling the command, contact a moderator.");
}
}
};

View file

@ -0,0 +1,140 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
module.exports = class SetModlogsCommand extends Command {
constructor(client) {
super(client, {
name: "releasenotes",
memberName: "releasenotes",
group: "lbry",
aliases: ["rn"],
description: "Sends the latest releasenotes to the channel.",
examples: ["releasenotes"],
guildOnly: true
});
}
hasPermission(msg) {
return this.client.isOwner(msg.author) || msg.member.hasPermission("ADMINISTRATOR");
}
async run(msg) {
try {
let githubResponse = await this.client.request({
url: "https://api.github.com/repos/lbryio/lbry-desktop/releases/latest",
headers: {
"Content-Type": "application/json",
"User-Agent": "Super Agent/0.0.1"
},
cacheKey: "https://api.github.com/repos/lbryio/lbry-desktop/releases/latest",
cacheTTL: 1,
cacheLimit: 1,
resolveWithFullResponse: false
});
githubResponse = JSON.parse(githubResponse);
const releaseEmbed = new RichEmbed();
if (githubResponse.body.length < 2000) {
releaseEmbed
.setTitle(`*Download ${githubResponse.name} here!*`)
.addField("Release information", githubResponse.body, true)
.setURL(githubResponse.html_url)
.setTimestamp(githubResponse.published_at)
.setColor(7976557)
.setAuthor(`LBRY Desktop release notes for ${githubResponse.name}`, "http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png", githubResponse.html_url);
return msg.embed(releaseEmbed) && msg.delete();
}
const message = githubResponse.body
.trim()
.split("###")
.filter(n => n !== "");
const releasemessage1 = message[0];
const releasemessage2 = message[1];
const releasemessage3 = message[2];
const releasemessage4 = message[3];
const releasemessage5 = message[4];
const message1 = {
embed: {
title: `*Download ${githubResponse.name} here!*`,
description: releasemessage1,
url: githubResponse.html_url,
color: 7976557,
timestamp: githubResponse.published_at,
author: {
name: `LBRY Desktop Release Notes for ${githubResponse.name}`,
icon_url: "http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png"
},
footer: {
icon_url: "https://i.imgur.com/yWf5USu.png",
text: "LBRY Desktop Updated "
}
}
};
const message2 = {
embed: {
description: releasemessage2,
color: 7976557,
timestamp: githubResponse.published_at,
author: {
icon_url: "http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png"
},
footer: {
icon_url: "https://i.imgur.com/yWf5USu.png",
text: "LBRY Desktop Updated "
}
}
};
const message3 = {
embed: {
description: releasemessage3,
color: 7976557,
timestamp: githubResponse.published_at,
author: {
icon_url: "http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png"
},
footer: {
icon_url: "https://i.imgur.com/yWf5USu.png",
text: "LBRY Desktop Updated "
}
}
};
const message4 = {
embed: {
description: releasemessage4,
color: 7976557,
timestamp: githubResponse.published_at,
author: {
icon_url: "http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png"
},
footer: {
icon_url: "https://i.imgur.com/yWf5USu.png",
text: "LBRY Desktop Updated "
}
}
};
const message5 = {
embed: {
description: releasemessage5,
color: 7976557,
timestamp: githubResponse.published_at,
author: {
icon_url: "http://www.pngall.com/wp-content/uploads/2016/04/Github-PNG-Image.png"
},
footer: {
icon_url: "https://i.imgur.com/yWf5USu.png",
text: "LBRY Desktop Updated "
}
}
};
msg.channel.send(message1);
msg.channel.send(message2);
msg.channel.send(message3);
msg.channel.send(message4);
msg.channel.send(message5);
msg.delete();
} catch (e) {
console.log(e);
return msg.reply("Could not get the latest releasenotes from github API.");
}
}
};

100
src/commands/lbry/stats.js Normal file
View file

@ -0,0 +1,100 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { locked } from "../../components/util";
module.exports = class StatsCommand extends Command {
constructor(client) {
super(client, {
name: "stats",
aliases: ["lbrystats"],
group: "lbry",
memberName: "lbrystats",
description: "Displays list of current Market stats",
throttling: {
usages: 2,
duration: 3
},
guildOnly: true,
args: null
});
}
async run(msg) {
const lockStatus = await locked("stats", msg);
if (lockStatus) {
let channelList = "";
lockStatus.forEach(channel => {
channelList += `<#${channel}> `;
});
return msg.reply(`This command cannot be ran from this channel, only from: ${channelList}`);
}
try {
// Extract the URL(s).
const { body, headers } = await this.client.request({
url: "https://api.coinmarketcap.com/v2/ticker/1298/?convert=BTC",
cacheKey: "https://api.coinmarketcap.com/v2/ticker/1298/?convert=BTC",
cacheTTL: 120000,
cacheLimit: 3,
resolveWithFullResponse: true
});
const { data } = JSON.parse(body);
const { rank } = data;
const priceUSD = Number(data.quotes.USD.price);
const priceBTC = Number(data.quotes.BTC.price);
const marketCapUSD = Number(data.quotes.USD.market_cap);
const circulatingSupply = Number(data.circulating_supply);
const totalSupply = Number(data.total_supply);
const percentChange1h = Number(data.quotes.USD.percent_change_1h);
const percentChange24h = Number(data.quotes.USD.percent_change_24h);
const volume24USD = Number(data.quotes.USD.volume_24h);
const dt = new Date();
const timestamp = dt.toUTCString();
let hrIndicator = ":thumbsup:";
let dayIndicator = ":thumbsup:";
if (percentChange1h < 0) {
hrIndicator = ":thumbsdown:";
}
if (percentChange24h < 0) {
dayIndicator = ":thumbsdown:";
}
const url2Response = await this.client.request({
url: "https://api.coinmarketcap.com/v2/ticker/1298/?convert=GBP",
cacheKey: "https://api.coinmarketcap.com/v2/ticker/1298/?convert=GBP",
cacheTTL: 120000,
cacheLimit: 3,
resolveWithFullResponse: true
});
const priceGBP = JSON.parse(url2Response.body).data.quotes.GBP.price;
const url3Response = await this.client.request({
url: "https://api.coinmarketcap.com/v2/ticker/1298/?convert=EUR",
cacheKey: "https://api.coinmarketcap.com/v2/ticker/1298/?convert=EUR",
cacheTTL: 120000,
cacheLimit: 3,
resolveWithFullResponse: true
});
const priceEUR = JSON.parse(url3Response.body).data.quotes.EUR.price;
const statsEmbed = new RichEmbed();
statsEmbed
.setAuthor("Coin Market Cap Stats (LBC)", "https://i.imgur.com/yWf5USu.png", "https://coinmarketcap.com/currencies/library-credit/")
.setColor(7976557)
.addField("Rank", rank, true)
.addField("Market Cap", `$${this.numberWithCommas(marketCapUSD)}`, true)
.addField("Total Supply", `${this.numberWithCommas(totalSupply)} LBC`, true)
.addField("Circulating Supply", `${this.numberWithCommas(circulatingSupply)} LBC`, true)
.addField("24 Hour Volume", `$${volume24USD}`, true)
.addField("Price in BTC", `${priceBTC.toFixed(8)} BTC`, true)
.addField("Price in USD", `$${priceUSD.toFixed(2)}`, true)
.addField("Price in EUR", `${priceEUR.toFixed(2)}`, true)
.addField("Price in GBP", `£${priceGBP.toFixed(2)}`, true)
.addField("Change the last hour", `${percentChange1h} ${hrIndicator}`, false)
.addField("Change the last day", `${percentChange24h} ${dayIndicator}`, false);
return msg.embed(statsEmbed);
} catch (e) {
console.log(e);
msg.channel.send("Could not connect to the API, try again later.");
}
}
numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
};

View file

@ -0,0 +1,24 @@
import { Command } from "discord.js-commando";
module.exports = class SupportCommand extends Command {
constructor(client) {
super(client, {
name: "close",
memberName: "suppbot",
group: "lbry",
description: "Helps a user close a ticket.",
guildOnly: true,
throttling: {
usages: 2,
duration: 3
}
});
}
run(msg) {
msg.channel.send("-close").catch(console.error);
setTimeout(() => {
msg.channel.send("-close").catch(console.error);
}, 4000);
}
};

View file

@ -0,0 +1,73 @@
import { Command } from "discord.js-commando";
import { inPrivate } from "../../components/util.js";
const commandCanBeLocked = ["stats", "hash", "rank"];
module.exports = class IDCommand extends Command {
constructor(client) {
super(client, {
name: "locktochannel",
aliases: ["ltc"],
group: "moderation",
memberName: "locktochannel",
description: "Lock a specific command to a specific channel.",
examples: ["locktochannel add stats #general"],
throttling: {
usages: 2,
duration: 3
},
guildOnly: true,
args: [
{
key: "action",
label: "action",
prompt: "What action would you like to preform? (`add`, `remove` or list.)",
type: "string",
infinite: false
},
{
key: "command",
prompt: "What command would you like to lock?",
type: "string"
},
{
key: "channel",
prompt: "What channel do you want it to be locked to?",
default: "#general",
type: "channel"
}
]
});
}
async run(msg, { action, command, channel }) {
if (!msg.guild.member(msg.author).hasPermission("MANAGE_ROLES", false, true, true)) return msg.reply(`You do not have permission to perform this action! You need the MANAGE_CHANNELS permission to use this command.`);
if (!channel.id) return msg.reply("Could not find that channel, please try again.");
if (!commandCanBeLocked.includes(command)) return msg.reply(`That is not a command that you can lock.`);
if (action.toLowerCase() === "add") {
const guildLocks = await msg.guild.settings.get(`lock_${command}`, []);
if (guildLocks.includes(channel.id)) return msg.reply("That command is already locked to that channel!");
guildLocks.push(channel.id);
await msg.guild.settings.set(`lock_${command}`, guildLocks);
return msg.reply(`Locked the ${command} command to <#${channel.id}>.`);
}
if (action.toLowerCase() === "remove") {
const guildLocks = await msg.guild.settings.get(`lock_${command}`, []);
if (!guildLocks.includes(channel.id)) return msg.reply("That command is not locked to that channel.");
guildLocks.splice(guildLocks.indexOf(command), 1);
await msg.guild.settings.set(`lock_${command}`, guildLocks);
return msg.reply(`Removed the lock of ${command} command for channel <#${channel.id}>.`);
}
/* Not in a working stage yet.
if (action.toLowerCase() === "list") {
const guildLocks = await msg.guild.settings.get(`lock_${command}`, []);
let channelList = "";
guildLocks.forEach(rank => {
channelList += `${rank}\n`;
});
const listEmbed = new RichEmbed();
listEmbed.setAuthor("Wunderbot | Lock Controller").setColor(7976557);
if (channelList) listEmbed.addField("Command is locked to:", channelList, true);
return msg.embed(listEmbed);
} */
}
};

View file

@ -0,0 +1,79 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { oneLine, stripIndents } from "common-tags";
import { deleteCommandMessages, stopTyping, startTyping } from "../../components/util.js";
module.exports = class ModLogsCommand extends Command {
constructor(client) {
super(client, {
name: "modlogs",
memberName: "modlogs",
group: "moderation",
aliases: ["togglemod"],
description: "Toggle mod logs in the mod-logs (or by you configured with setmodlogs) channel",
format: "BooleanResolvable",
examples: ["modlogs {option}", "modlogs enable"],
guildOnly: true,
throttling: {
usages: 2,
duration: 3
},
args: [
{
key: "option",
prompt: "Enable or disable modlogs?",
type: "boolean",
validate: bool => {
const validBools = ["true", "t", "yes", "y", "on", "enable", "enabled", "1", "+", "false", "f", "no", "n", "off", "disable", "disabled", "0", "-"];
if (validBools.includes(bool.toLowerCase())) {
return true;
}
return stripIndents`Has to be one of ${validBools.map(val => `\`${val}\``).join(", ")}
Respond with your new selection or`;
}
}
]
});
}
hasPermission(msg) {
return this.client.isOwner(msg.author) || msg.member.hasPermission("ADMINISTRATOR");
}
run(msg, { option }) {
startTyping(msg);
const modlogChannel = msg.guild.settings.get("modlogchannel", msg.guild.channels.find(c => c.name === "mod-logs") ? msg.guild.channels.find(c => c.name === "mod-logs").id : null);
const modlogsEmbed = new RichEmbed();
msg.guild.settings.set("modlogs", option);
modlogsEmbed
.setColor("#3DFFE5")
.setAuthor(msg.author.tag, msg.author.displayAvatarURL)
.setDescription(
stripIndents`
**Action:** Moderator logs are now ${option ? "enabled" : "disabled"}
**Details:** Please ensure you configure modlogs with \`${msg.guild.commandPrefix}setmodlogs\` or have a channel named \`mod-logs\`
`
)
.setTimestamp();
if (msg.guild.settings.get("modlogs", true)) {
if (!msg.guild.settings.get("hasSentModLogMessage", false)) {
msg.reply(oneLine`📃 I can keep a log of moderator actions if you create a channel named \'mod-logs\'
(or some other name configured by the ${msg.guild.commandPrefix}setmodlogs command) and give me access to it.
This message will only show up this one time and never again after this so if you desire to set up mod logs make sure to do so now.`);
msg.guild.settings.set("hasSentModLogMessage", true);
}
modlogChannel ? msg.guild.channels.get(modlogChannel).send("", { embed: modlogsEmbed }) : null;
}
deleteCommandMessages(msg, this.client);
stopTyping(msg);
return msg.embed(modlogsEmbed);
}
};

View file

@ -0,0 +1,75 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { oneLine } from "common-tags";
module.exports = class PubRanksCommand extends Command {
constructor(client) {
super(client, {
name: "pubranks",
aliases: ["editranks", "editroles", "pubrank", "editroleme"],
group: "moderation",
memberName: "pubranks",
description: "Manages a server's public roles.",
details: oneLine`
Do you want to have an opt-in only NSFW channel? A role that you can ping to avoid pinging everyone?
This command allows for management of a server's public roles.
Note: Adding and removing public roles must be done by someone with the MANAGE_ROLES permission.
`,
examples: ["pubranks add ping"],
args: [
{
key: "action",
label: "action",
prompt: "What action would you like to preform? (`add`, `remove`, or `list`)",
type: "string",
infinite: false
},
{
key: "rank",
label: "rank",
prompt: "",
type: "string",
default: "",
infinite: false
}
],
guildOnly: true,
guarded: true
});
}
async run(msg, args) {
if (args.action.toLowerCase() === "add") {
if (!msg.guild.member(msg.author).hasPermission("MANAGE_ROLES", false, true, true)) return msg.reply(`You do not have permission to perform this action! Did you mean \`${msg.guild.commandPrefix}rank give\`?`);
const guildRanks = await msg.guild.settings.get("ranks", []);
const rankToAdd = msg.guild.roles.find("name", args.rank);
if (rankToAdd === null) return msg.reply("That is not a role! Was your capatalization and spelling correct?");
guildRanks.push(args.rank);
await msg.guild.settings.set("ranks", guildRanks);
msg.reply("Role added.");
} else if (args.action.toLowerCase() === "remove" || args.action.toLowerCase() === "delete") {
if (!msg.guild.member(msg.author).hasPermission("MANAGE_ROLES", false, true, true)) return msg.reply(`You do not have permission to perform this action! Did you mean\`${msg.guild.commandPrefix}rank take\`?`);
const guildRanks = await msg.guild.settings.get("ranks", []);
const rankIndex = guildRanks.indexOf(args.rank);
const rankToRemove = msg.guild.roles.find("name", args.rank);
if (rankToRemove === null) return msg.reply("That is not a role! Was your capatalization and spelling correct?");
guildRanks.splice(rankIndex, 1);
msg.reply("Role removed.");
-await msg.guild.settings.set("ranks", guildRanks);
} else if (args.action.toLowerCase() === "list") {
const guildRanks = await msg.guild.settings.get("ranks", null);
if (!guildRanks) return msg.reply(`There are no public roles! Maybe try adding some? Do \`${msg.guild.commandPrefix}pubranks add role\` to add a role.`);
let rankList = "";
guildRanks.forEach(rank => {
rankList += `${rank}\n`;
});
const rankEmbed = new RichEmbed();
rankEmbed.setAuthor("Wunderbot | Rank Controller").setColor(7976557);
if (rankList) rankEmbed.addField("Ranks available:", rankList, true);
msg.embed(rankEmbed);
} else {
return msg.reply(`Invalid command usage. Please use \`add\`, \`remove\`, or \`list\`.
**NOTE:** If you are trying to give yourself a role, do \`${msg.guild.commandPrefix}rank give role\`.`);
}
}
};

View file

@ -0,0 +1,69 @@
import { Command } from "discord.js-commando";
import { RichEmbed } from "discord.js";
import { oneLine, stripIndents } from "common-tags";
import { deleteCommandMessages, stopTyping, startTyping } from "../../components/util.js";
module.exports = class SetModlogsCommand extends Command {
constructor(client) {
super(client, {
name: "setmodlogs",
memberName: "setmodlogs",
group: "moderation",
aliases: ["setmod"],
description: 'Set the modlogs channel used for logging mod commands. Ensure to enable modlogs with the "modlogs" command.',
format: "ChannelID|ChannelName(partial or full)",
examples: ["setmodlogs mod-logs"],
guildOnly: true,
throttling: {
usages: 2,
duration: 3
},
args: [
{
key: "channel",
prompt: "What channel should I set for member logs? (make sure to start with a # when going by name)",
type: "channel"
}
]
});
}
hasPermission(msg) {
return this.client.isOwner(msg.author) || msg.member.hasPermission("ADMINISTRATOR");
}
run(msg, { channel }) {
startTyping(msg);
const modlogChannel = msg.guild.settings.get("modlogchannel", msg.guild.channels.find(c => c.name === "mod-logs") ? msg.guild.channels.find(c => c.name === "mod-logs").id : null);
const setModLogsEmbed = new RichEmbed();
msg.guild.settings.set("modlogchannel", channel.id);
setModLogsEmbed
.setColor("#3DFFE5")
.setAuthor(msg.author.tag, msg.author.displayAvatarURL)
.setDescription(
stripIndents`
**Action:** Mod logs channel changed
**Channel:** <#${channel.id}>
`
)
.setTimestamp();
if (msg.guild.settings.get("modlogs", true)) {
if (!msg.guild.settings.get("hasSentModLogMessage", false)) {
msg.reply(oneLine`📃 I can keep a log of moderator actions if you create a channel named \'mod-logs\'
(or some other name configured by the ${msg.guild.commandPrefix}setmodlogs command) and give me access to it.
This message will only show up this one time and never again after this so if you desire to set up mod logs make sure to do so now.`);
msg.guild.settings.set("hasSentModLogMessage", true);
}
modlogChannel ? msg.guild.channels.get(modlogChannel).send("", { embed: setModLogsEmbed }) : null;
}
deleteCommandMessages(msg, this.client);
stopTyping(msg);
return msg.embed(setModLogsEmbed);
}
};

View file

@ -0,0 +1,43 @@
import { Command } from "discord.js-commando";
module.exports = class WelcomeCommand extends Command {
constructor(client) {
super(client, {
name: "welcome",
aliases: ["wel"],
group: "moderation",
memberName: "welcome",
description: "Sends the servers welcome msgs to a user, call with enable/disable instead of user to enable sending on join.",
examples: ["welcome @Fillerino", "welcome enable/disable"],
args: [
{
key: "user",
label: "user",
prompt: "Which user would you like to send to? (type enable/disable instead of user to make it auto send on join)",
type: "string",
infinite: false
}
],
guildOnly: true
});
}
hasPermission(msg) {
return this.client.isOwner(msg.author) || msg.member.hasPermission("ADMINISTRATOR");
}
async run(msg, { user }) {
if (user.toLowerCase() === "enable") {
await msg.guild.settings.set("welcomemsgenabled", true);
return msg.reply("Enabled automatic sending of welcome message.");
}
if (user.toLowerCase() === "disable") {
await msg.guild.settings.set("welcomemsgenabled", false);
return msg.reply("Disabled automatic sending of welcome message.");
}
if ((await msg.guild.settings.get("welcomemsg", [])) >= 1) {
const embeds = await msg.guild.settings.get("welcomemsg");
embeds.forEach(e => msg.mentions.members.first().send({ embed: e }));
}
}
};

191
src/components/util.js Normal file
View file

@ -0,0 +1,191 @@
import emojis from "emoji-regex";
const locked = async function(cmd, msg) {
const lockChannels = await msg.guild.settings.get(`lock_${cmd}`, []);
if (lockChannels.length < 1) return false;
if (lockChannels.includes(msg.channel.id)) return false;
return lockChannels;
};
const arrayClean = function(deleteValue, array) {
for (let val in array) {
if (array[val] === deleteValue) {
array.splice(val, 1);
val -= 1;
}
}
return array;
};
const capitalizeFirstLetter = function(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
};
const countCaps = function(capcount, total) {
return capcount.replace(/[^A-Z]/g, "").length / total.length * 100;
};
const countEmojis = function(str) {
const customEmojis = /<a{0,1}:[\S]+:[0-9]{18}>/gim;
const customMatch = str.match(customEmojis);
const unicodeEmojis = emojis();
const unicodeMatch = str.match(unicodeEmojis);
let counter = 0;
unicodeMatch ? (counter += unicodeMatch.length) : null;
customMatch ? (counter += customMatch.length) : null;
return counter;
};
const countMentions = function(str) {
const mentions = /^<@![0-9]{18}>$/gim;
const mentionsMatch = str.match(mentions);
let counter = 0;
mentionsMatch ? (counter += mentionsMatch.length) : null;
return counter;
};
const deleteCommandMessages = function(msg, client) {
// eslint-disable-line consistent-return
if (msg.deletable && client.provider.get(msg.guild, "deletecommandmessages", false)) {
return msg.delete();
}
};
const removeDiacritics = function(string) {
// eslint-disable-next-line max-len, object-property-newline
const defaultDiacriticsRemovalMap = [
{
base: "A",
letters: /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g
},
{ base: "AA", letters: /[\uA732]/g },
{ base: "AE", letters: /[\u00C6\u01FC\u01E2]/g },
{ base: "AO", letters: /[\uA734]/g },
{ base: "AU", letters: /[\uA736]/g },
{ base: "AV", letters: /[\uA738\uA73A]/g },
{ base: "AY", letters: /[\uA73C]/g },
{ base: "B", letters: /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g },
{ base: "C", letters: /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g },
{ base: "D", letters: /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g },
{ base: "DZ", letters: /[\u01F1\u01C4]/g },
{ base: "Dz", letters: /[\u01F2\u01C5]/g },
{ base: "E", letters: /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g },
{ base: "F", letters: /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g },
{ base: "G", letters: /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g },
{ base: "H", letters: /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g },
{ base: "I", letters: /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g },
{ base: "J", letters: /[\u004A\u24BF\uFF2A\u0134\u0248]/g },
{ base: "K", letters: /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g },
{ base: "L", letters: /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g },
{ base: "LJ", letters: /[\u01C7]/g },
{ base: "Lj", letters: /[\u01C8]/g },
{ base: "M", letters: /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g },
{ base: "N", letters: /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g },
{ base: "NJ", letters: /[\u01CA]/g },
{ base: "Nj", letters: /[\u01CB]/g },
{
base: "O",
letters: /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g
},
{ base: "OI", letters: /[\u01A2]/g },
{ base: "OO", letters: /[\uA74E]/g },
{ base: "OU", letters: /[\u0222]/g },
{ base: "P", letters: /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g },
{ base: "Q", letters: /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g },
{ base: "R", letters: /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g },
{ base: "S", letters: /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g },
{ base: "T", letters: /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g },
{ base: "TZ", letters: /[\uA728]/g },
{
base: "U",
letters: /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g
},
{ base: "V", letters: /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g },
{ base: "VY", letters: /[\uA760]/g },
{ base: "W", letters: /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g },
{ base: "X", letters: /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g },
{ base: "Y", letters: /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g },
{ base: "Z", letters: /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g },
{
base: "a",
letters: /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g
},
{ base: "aa", letters: /[\uA733]/g },
{ base: "ae", letters: /[\u00E6\u01FD\u01E3]/g },
{ base: "ao", letters: /[\uA735]/g },
{ base: "au", letters: /[\uA737]/g },
{ base: "av", letters: /[\uA739\uA73B]/g },
{ base: "ay", letters: /[\uA73D]/g },
{ base: "b", letters: /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g },
{ base: "c", letters: /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g },
{ base: "d", letters: /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g },
{ base: "dz", letters: /[\u01F3\u01C6]/g },
{ base: "e", letters: /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g },
{ base: "f", letters: /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g },
{ base: "g", letters: /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g },
{ base: "h", letters: /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g },
{ base: "hv", letters: /[\u0195]/g },
{ base: "i", letters: /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g },
{ base: "j", letters: /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g },
{ base: "k", letters: /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g },
{ base: "l", letters: /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g },
{ base: "lj", letters: /[\u01C9]/g },
{ base: "m", letters: /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g },
{ base: "n", letters: /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g },
{ base: "nj", letters: /[\u01CC]/g },
{
base: "o",
letters: /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g
},
{ base: "oi", letters: /[\u01A3]/g },
{ base: "ou", letters: /[\u0223]/g },
{ base: "oo", letters: /[\uA74F]/g },
{ base: "p", letters: /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g },
{ base: "q", letters: /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g },
{ base: "r", letters: /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g },
{ base: "s", letters: /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g },
{ base: "t", letters: /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g },
{ base: "tz", letters: /[\uA729]/g },
{
base: "u",
letters: /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g
},
{ base: "v", letters: /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g },
{ base: "vy", letters: /[\uA761]/g },
{ base: "w", letters: /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g },
{ base: "x", letters: /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g },
{ base: "y", letters: /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g },
{ base: "z", letters: /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g }
];
for (let i = 0; i < defaultDiacriticsRemovalMap.length; ++i) {
string = string.replace(defaultDiacriticsRemovalMap[i].letters, defaultDiacriticsRemovalMap[i].base);
}
return string;
};
const stopTyping = function(msg) {
msg.channel.stopTyping(true);
};
const startTyping = function(msg) {
msg.channel.startTyping(1);
};
module.exports = {
locked,
arrayClean,
countCaps,
countEmojis,
countMentions,
capitalizeFirstLetter,
deleteCommandMessages,
removeDiacritics,
startTyping,
stopTyping
};

View file

@ -0,0 +1,12 @@
import Discord from "discord.js";
module.exports = async function checkCustomCommand(msg, client) {
if (msg.channel.type === "dm") return;
if (msg.author.bot) return;
const custmcmds = await msg.guild.settings.get("custmcmds", {});
Object.keys(custmcmds).forEach(cmd => {
if (msg.content.toLowerCase().indexOf(cmd.toLowerCase()) >= 0) {
msg.channel.send("", new Discord.RichEmbed(custmcmds[cmd].embedbundle));
}
});
};

View file

@ -0,0 +1,10 @@
module.exports = async function checkWelcomeEvent(member, client) {
try {
if ((await member.guild.settings.get("welcomemsgenabled", false)) && (await member.guild.settings.get("welcomemsg", [])) >= 1) {
const embeds = await member.guild.settings.get("welcomemsg");
embeds.forEach(e => member.send({ embed: e }));
}
} catch (e) {
console.log("Could not send help message to a user.");
}
};

3
src/index.js Normal file
View file

@ -0,0 +1,3 @@
import Wunderbot from "./bot";
const wunderbot = new Wunderbot();

76
src/web/controlPanel.js Normal file
View file

@ -0,0 +1,76 @@
import Discord from "discord.js";
import express from "express";
import session from "express-session";
import passport from "passport";
import DiscordStrat from "passport-discord";
import config from "config";
const app = express();
const host = process.env.HOST || "127.0.0.1";
const port = process.env.PORT || 3000;
// Setup passport for discord usage
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((obj, done) => {
done(null, obj);
});
const scopes = ["identify", "guilds"];
passport.use(
new DiscordStrat(
{
clientID: config.get("Web.clientID"),
clientSecret: config.get("Web.clientSecret"),
callbackURL: "http://localhost:3000/callback",
scope: scopes
},
(accessToken, refreshToken, profile, done) => {
process.nextTick(() => done(null, profile));
}
)
);
// Function to check authentication status
function checkAuth(req, res, next) {
if (req.isAuthenticated()) return next();
res.send("not logged in :(");
}
// Returns all the guilds a user can control
function canControlGuilds(guilds) {
return guilds.filter(g => {
gp = new Discord.Permissions(null, g.permissions);
return gp.has("MANAGE_GUILD");
});
}
// Lets get some sessions going!
app.use(
session({
secret: "keyboard cat",
resave: false,
saveUninitialized: true
})
);
app.use(passport.initialize());
app.use(passport.session());
// Standard routes
app.get("/login", passport.authenticate("discord", { scope: scopes }), (req, res) => {});
app.get(
"/callback",
passport.authenticate("discord", { failureRedirect: "/me" }),
(req, res) => {
res.redirect("/me");
} // auth success
);
app.get("/logout", (req, res) => {
req.logout();
res.redirect("/");
});
app.get("/me", checkAuth, (req, res) => {
res.json(req.user);
});
// Time to run?!
module.exports = async function run(client) {
app.listen(port, host);
console.log(`Webserver listening on ${host}:${port}`); // eslint-disable-line no-console
};

3955
yarn.lock

File diff suppressed because it is too large Load diff