2016-07-23 23:58:35 +12:00
import discord
2016-07-09 13:27:19 +12:00
from discord . ext import commands
2017-02-12 12:14:57 +13:00
from . import utils
2016-07-23 23:58:35 +12:00
2016-07-09 13:27:19 +12:00
import random
2016-07-11 01:58:46 +12:00
import re
2016-07-19 11:25:03 +12:00
import calendar
2016-08-07 10:20:16 +12:00
import pendulum
2016-07-19 11:25:03 +12:00
import datetime
2017-04-21 15:03:52 +12:00
import psutil
2016-07-09 13:27:19 +12:00
2016-07-19 11:29:40 +12:00
2017-05-01 11:56:02 +12:00
class Miscallaneous :
2016-07-23 23:58:35 +12:00
""" Core commands, these are the miscallaneous commands that don ' t fit into other categories ' """
2016-07-11 09:18:30 +12:00
2016-07-09 13:27:19 +12:00
def __init__ ( self , bot ) :
self . bot = bot
2017-04-21 15:03:52 +12:00
self . process = psutil . Process ( )
self . process . cpu_percent ( )
2016-12-02 16:58:28 +13:00
2018-10-09 09:40:10 +13:00
def _command_signature ( self , cmd ) :
result = [ cmd . qualified_name ]
if cmd . usage :
result . append ( cmd . usage )
return ' ' . join ( result )
params = cmd . clean_params
if not params :
return ' ' . join ( result )
for name , param in params . items ( ) :
if param . default is not param . empty :
# We don't want None or '' to trigger the [name=value] case and instead it should
# do [name] since [name=None] or [name=] are not exactly useful for the user.
should_print = param . default if isinstance ( param . default , str ) else param . default is not None
if should_print :
result . append ( f ' [ { name } = { param . default !r} ] ' )
else :
result . append ( f ' [ { name } ] ' )
elif param . kind == param . VAR_POSITIONAL :
result . append ( f ' [ { name } ...] ' )
else :
result . append ( f ' < { name } > ' )
return ' ' . join ( result )
2017-03-06 15:45:44 +13:00
@commands.command ( )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2018-10-09 09:40:10 +13:00
async def help ( self , ctx , * , entity : str = None ) :
chunks = [ ]
2016-12-20 05:31:45 +13:00
2018-10-09 09:40:10 +13:00
if entity :
entity = self . bot . get_cog ( entity ) or self . bot . get_command ( entity )
if entity is None :
fmt = " Hello! Here is a list of the sections of commands that I have (there are a lot of commands so just start with the sections...I know, I ' m pretty great) \n "
fmt + = " To use a command ' s paramaters, you need to know the notation for them: \n "
fmt + = " \t <argument> This means the argument is __**required**__. \n "
fmt + = " \t [argument] This means the argument is __**optional**__. \n "
fmt + = " \t [A|B] This means the it can be __**either A or B**__. \n "
fmt + = " \t [argument...] This means you can have multiple arguments. \n "
fmt + = " \n **Type ` {} help section` to get help on a specific section** \n " . format ( ctx . prefix )
fmt + = " **CASE MATTERS** Sections are in `Title Case` and commands are in `lower case` \n \n "
chunks . append ( fmt )
2017-05-01 11:56:02 +12:00
2018-10-09 09:40:10 +13:00
cogs = sorted ( self . bot . cogs . values ( ) , key = lambda c : c . __class__ . __name__ )
for cog in cogs :
tmp = " ** {} ** \n " . format ( cog . __class__ . __name__ )
if cog . __doc__ :
tmp + = " \t {} \n " . format ( cog . __doc__ )
if len ( chunks [ len ( chunks ) - 1 ] + tmp ) > 2000 :
chunks . append ( tmp )
2018-10-05 08:14:09 +13:00
else :
2018-10-09 09:40:10 +13:00
chunks [ len ( chunks ) - 1 ] + = tmp
elif isinstance ( entity , ( commands . core . Command , commands . core . Group ) ) :
tmp = " ** {} ** " . format ( self . _command_signature ( entity ) )
2018-10-09 13:29:20 +13:00
tmp + = " \n {} " . format ( entity . help )
2018-10-09 09:40:10 +13:00
chunks . append ( tmp )
else :
cmds = sorted ( ctx . bot . get_cog_commands ( entity . __class__ . __name__ ) , key = lambda c : c . name )
fmt = " Here are a list of commands under the section {} \n " . format ( entity . __class__ . __name__ )
fmt + = " Type ` {} help command` to get more help on a specific command \n \n " . format ( ctx . prefix )
2017-05-01 11:56:02 +12:00
2018-10-09 09:40:10 +13:00
chunks . append ( fmt )
for command in cmds :
for subcommand in utils . get_all_subcommands ( command ) :
tmp = " ** {} ** \n \t {} \n " . format ( subcommand . qualified_name , subcommand . short_doc )
if len ( chunks [ len ( chunks ) - 1 ] + tmp ) > 2000 :
chunks . append ( tmp )
else :
chunks [ len ( chunks ) - 1 ] + = tmp
2018-10-09 13:28:47 +13:00
if utils . dev_server :
tmp = " \n \n If I ' m having issues, then please visit the dev server and ask for help. {} " . format ( utils . dev_server )
if len ( chunks [ len ( chunks ) - 1 ] + tmp ) > 2000 :
chunks . append ( tmp )
else :
chunks [ len ( chunks ) - 1 ] + = tmp
2018-10-11 13:34:52 +13:00
if len ( chunks ) == 1 and len ( chunks [ 0 ] ) < 1000 :
destination = ctx . channel
else :
destination = ctx . author
2018-10-09 09:40:10 +13:00
try :
for chunk in chunks :
2018-10-11 13:34:52 +13:00
await destination . send ( chunk )
2018-10-09 09:40:10 +13:00
except ( discord . Forbidden , discord . HTTPException ) :
await ctx . send ( " I cannot DM you, please allow DM ' s from this server to run this command " )
else :
2018-10-11 13:36:39 +13:00
if ctx . guild and destination == ctx . author :
2018-10-09 09:40:10 +13:00
await ctx . send ( " I have just DM ' d you some information about me! " )
2017-05-01 11:56:02 +12:00
2018-10-11 12:46:34 +13:00
@commands.command ( )
@utils.can_run ( send_messages = True )
async def ping ( self , ctx ) :
""" Returns the latency between the server websocket, and between reading messages """
msg_latency = datetime . datetime . utcnow ( ) - ctx . message . created_at
fmt = " Message latency {0:.2f} seconds " . format ( msg_latency . seconds + msg_latency . microseconds / 1000000 )
fmt + = " \n Websocket latency {0:.2f} seconds " . format ( self . bot . latency )
await ctx . send ( fmt )
2017-12-03 11:21:39 +13:00
@commands.command ( aliases = [ " coin " ] )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-12-03 11:21:39 +13:00
async def coinflip ( self , ctx ) :
""" Flips a coin and responds with either heads or tails
EXAMPLE : ! coinflip
RESULT : Heads ! """
result = " Heads! " if random . SystemRandom ( ) . randint ( 0 , 1 ) else " Tails! "
await ctx . send ( result )
2017-05-01 11:56:02 +12:00
@commands.command ( )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-05-01 11:56:02 +12:00
async def say ( self , ctx , * , msg : str ) :
""" Tells the bot to repeat what you say
EXAMPLE : ! say I really like orange juice
RESULT : I really like orange juice """
fmt = " \u200B {} " . format ( msg )
await ctx . send ( fmt )
try :
await ctx . message . delete ( )
2018-09-24 07:23:27 +12:00
except Exception :
2017-05-01 11:56:02 +12:00
pass
2016-11-29 16:52:45 +13:00
2016-07-19 11:25:03 +12:00
@commands.command ( )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-03-06 15:45:44 +13:00
async def calendar ( self , ctx , month : str = None , year : int = None ) :
2016-07-19 11:40:09 +12:00
""" Provides a printout of the current month ' s calendar
2016-11-29 16:51:18 +13:00
Provide month and year to print the calendar of that year and month
EXAMPLE : ! calendar january 2011 """
2016-08-27 01:23:02 +12:00
# calendar takes in a number for the month, not the words
# so we need this dictionary to transform the word to the number
2016-07-19 11:25:03 +12:00
months = {
" january " : 1 ,
" february " : 2 ,
" march " : 3 ,
" april " : 4 ,
" may " : 5 ,
" june " : 6 ,
" july " : 7 ,
" august " : 8 ,
" september " : 9 ,
" october " : 10 ,
" november " : 11 ,
" december " : 12
}
2016-08-16 15:30:52 +12:00
# In month was not passed, use the current month
2016-07-19 11:25:03 +12:00
if month is None :
month = datetime . date . today ( ) . month
else :
2016-07-19 11:30:52 +12:00
month = months . get ( month . lower ( ) )
2016-07-19 11:25:03 +12:00
if month is None :
2017-03-06 15:45:44 +13:00
await ctx . send ( " Please provide a valid Month! " )
2016-07-19 11:25:03 +12:00
return
2016-08-16 15:30:52 +12:00
# If year was not passed, use the current year
2016-07-19 11:25:03 +12:00
if year is None :
year = datetime . datetime . today ( ) . year
2016-08-16 15:30:52 +12:00
# Here we create the actual "text" calendar that we are printing
2016-07-19 11:25:03 +12:00
cal = calendar . TextCalendar ( ) . formatmonth ( year , month )
2017-03-06 15:45:44 +13:00
await ctx . send ( " ``` \n {} ``` " . format ( cal ) )
2016-08-27 01:23:02 +12:00
2017-05-01 11:56:02 +12:00
@commands.command ( aliases = [ ' about ' ] )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-03-06 15:45:44 +13:00
async def info ( self , ctx ) :
2016-08-14 10:46:15 +12:00
""" This command can be used to print out some of my information """
2016-09-06 14:52:14 +12:00
# fmt is a dictionary so we can set the key to it's output, then print both
# The only real use of doing it this way is easier editing if the info
# in this command is changed
2016-08-27 01:23:02 +12:00
2016-11-21 14:27:34 +13:00
# Create the original embed object
2017-07-12 07:34:45 +12:00
# Set the description include dev server (should be required) and the optional patreon link
description = " [Dev Server]( {} ) " . format ( utils . dev_server )
if utils . patreon_link :
2017-07-12 07:38:12 +12:00
description + = " \n [Patreon]( {} ) " . format ( utils . patreon_link )
2017-07-12 07:34:45 +12:00
# Now creat the object
opts = { ' title ' : ' Bonfire ' ,
' description ' : description ,
' colour ' : discord . Colour . green ( ) }
# Set the owner
2016-11-21 14:27:34 +13:00
embed = discord . Embed ( * * opts )
2017-07-12 07:34:45 +12:00
if hasattr ( self . bot , ' owner ' ) :
2017-07-12 07:36:24 +12:00
embed . set_author ( name = str ( self . bot . owner ) , icon_url = self . bot . owner . avatar_url )
2017-07-12 07:34:45 +12:00
# Setup the process statistics
2017-07-19 10:08:09 +12:00
name = " Process statistics "
2017-07-12 07:34:45 +12:00
value = " "
memory_usage = self . process . memory_full_info ( ) . uss / 1024 * * 2
cpu_usage = self . process . cpu_percent ( ) / psutil . cpu_count ( )
value + = ' Memory: {:.2f} MiB ' . format ( memory_usage )
value + = ' \n CPU: {} % ' . format ( cpu_usage )
if hasattr ( self . bot , ' uptime ' ) :
2018-09-24 07:23:27 +12:00
value + = " \n Uptime: {} " . format ( ( pendulum . now ( tz = " UTC " ) - self . bot . uptime ) . in_words ( ) )
2017-07-12 07:34:45 +12:00
embed . add_field ( name = name , value = value , inline = False )
# Setup the user and guild statistics
2017-07-19 10:08:09 +12:00
name = " User/Guild statistics "
2017-07-12 07:34:45 +12:00
value = " "
value + = " Channels: {} " . format ( len ( list ( self . bot . get_all_channels ( ) ) ) )
value + = " \n Users: {} " . format ( len ( self . bot . users ) )
value + = " \n Servers: {} " . format ( len ( self . bot . guilds ) )
embed . add_field ( name = name , value = value , inline = False )
# The game statistics
name = " Game statistics "
# To get the newlines right, since we're not sure what will and won't be included
# Lets make this one a list and join it at the end
value = [ ]
2016-11-21 14:27:34 +13:00
2017-05-05 10:33:10 +12:00
hm = self . bot . get_cog ( ' Hangman ' )
ttt = self . bot . get_cog ( ' TicTacToe ' )
bj = self . bot . get_cog ( ' Blackjack ' )
2017-07-12 07:34:45 +12:00
interaction = self . bot . get_cog ( ' Interaction ' )
2016-11-21 14:27:34 +13:00
2017-05-05 10:33:10 +12:00
if hm :
2017-07-12 07:34:45 +12:00
value . append ( " Hangman games: {} " . format ( len ( hm . games ) ) )
2017-05-05 10:33:10 +12:00
if ttt :
2017-07-12 07:34:45 +12:00
value . append ( " TicTacToe games: {} " . format ( len ( ttt . boards ) ) )
2017-05-05 10:33:10 +12:00
if bj :
2017-07-12 07:34:45 +12:00
value . append ( " Blackjack games: {} " . format ( len ( bj . games ) ) )
2017-05-05 10:33:10 +12:00
if interaction :
count_battles = 0
for battles in self . bot . get_cog ( ' Interaction ' ) . battles . values ( ) :
count_battles + = len ( battles )
2017-07-12 07:34:45 +12:00
value . append ( " Battles running: {} " . format ( len ( bj . games ) ) )
embed . add_field ( name = name , value = " \n " . join ( value ) , inline = False )
2016-08-27 01:23:02 +12:00
2017-03-06 15:45:44 +13:00
await ctx . send ( embed = embed )
2016-08-27 01:23:02 +12:00
2016-07-31 02:59:35 +12:00
@commands.command ( )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-03-06 15:45:44 +13:00
async def uptime ( self , ctx ) :
2016-11-29 16:51:18 +13:00
""" Provides a printout of the current bot ' s uptime
EXAMPLE : ! uptime
RESULT : A BAJILLION DAYS """
2017-03-11 11:19:18 +13:00
if hasattr ( self . bot , ' uptime ' ) :
2018-09-24 07:23:27 +12:00
await ctx . send ( " Uptime: ``` \n {} ``` " . format ( ( pendulum . now ( tz = " UTC " ) - self . bot . uptime ) . in_words ( ) ) )
2017-03-11 11:19:18 +13:00
else :
await ctx . send ( " I ' ve just restarted and not quite ready yet...gimme time I ' m not a morning pony :c " )
2016-07-19 11:25:03 +12:00
2016-08-08 02:03:36 +12:00
@commands.command ( aliases = [ ' invite ' ] )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-03-06 15:45:44 +13:00
async def addbot ( self , ctx ) :
2016-11-29 16:51:18 +13:00
""" Provides a link that you can use to add me to a server
EXAMPLE : ! addbot
RESULT : http : / / discord . gg / yo_mama """
2016-07-10 06:27:14 +12:00
perms = discord . Permissions . none ( )
perms . read_messages = True
perms . send_messages = True
perms . manage_roles = True
perms . ban_members = True
perms . kick_members = True
perms . manage_messages = True
perms . embed_links = True
perms . read_message_history = True
perms . attach_files = True
2017-03-06 15:45:44 +13:00
perms . speak = True
perms . connect = True
perms . attach_files = True
perms . add_reactions = True
2016-08-16 15:30:52 +12:00
app_info = await self . bot . application_info ( )
2017-07-03 15:18:33 +12:00
await ctx . send ( " Use this URL to add me to a server that you ' d like! \n < {} > "
2017-03-08 11:35:30 +13:00
. format ( discord . utils . oauth_url ( app_info . id , perms ) ) )
2016-07-18 09:10:12 +12:00
2017-06-07 20:30:19 +12:00
@commands.command ( enabled = False )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-03-06 15:45:44 +13:00
async def joke ( self , ctx ) :
2016-11-29 16:51:18 +13:00
""" Prints a random riddle
EXAMPLE : ! joke
RESULT : An absolutely terrible joke . """
2017-06-07 20:30:19 +12:00
# Currently disabled until I can find a free API
pass
2016-07-09 13:27:19 +12:00
2017-03-06 15:45:44 +13:00
@commands.command ( )
2018-09-24 10:34:14 +12:00
@utils.can_run ( send_messages = True )
2017-06-29 10:43:28 +12:00
async def roll ( self , ctx , * , notation : str = " d6 " ) :
2016-07-11 01:58:03 +12:00
""" Rolls a die based on the notation given
2016-11-29 16:51:18 +13:00
Format should be #d#
EXAMPLE : ! roll d50
RESULT : 51 : ^ ) """
2016-08-16 15:30:52 +12:00
# Use regex to get the notation based on what was provided
2016-07-11 01:58:03 +12:00
try :
2016-09-06 14:52:14 +12:00
# We do not want to try to convert the dice, because we want d# to
# be a valid notation
2016-07-25 03:34:04 +12:00
dice = re . search ( " ( \ d*)d( \ d*) " , notation ) . group ( 1 )
2016-07-12 06:16:18 +12:00
num = int ( re . search ( " ( \ d*)d( \ d*) " , notation ) . group ( 2 ) )
2017-06-29 10:43:28 +12:00
# Attempt to get addition/subtraction
add = re . search ( " \ + ?( \ d+) " , notation )
subtract = re . search ( " - ?( \ d+) " , notation )
2016-09-06 14:52:14 +12:00
# Check if something like ed3 was provided, or something else entirely
# was provided
2016-08-13 07:32:38 +12:00
except ( AttributeError , ValueError ) :
2017-03-06 15:45:44 +13:00
await ctx . send ( " Please provide the die notation in #d#! " )
2016-07-11 01:58:03 +12:00
return
2016-08-27 01:23:02 +12:00
2016-07-17 02:20:17 +12:00
# Dice will be None if d# was provided, assume this means 1d#
2016-07-25 03:34:04 +12:00
dice = dice or 1
2016-09-06 14:52:14 +12:00
# Since we did not try to convert to int before, do it now after we
# have it set
2016-07-25 03:34:04 +12:00
dice = int ( dice )
2017-07-24 14:15:01 +12:00
if dice > 30 :
await ctx . send ( " I ' m not rolling more than 30 dice, I have tiny hands " )
2016-07-11 01:58:03 +12:00
return
2016-07-12 06:16:18 +12:00
if num > 100 :
2017-03-06 15:45:44 +13:00
await ctx . send ( " What die has more than 100 sides? Please, calm down " )
2016-07-11 01:58:03 +12:00
return
2016-10-16 08:43:45 +13:00
if num < = 1 :
2017-03-06 15:45:44 +13:00
await ctx . send ( " A {} sided die? You know that ' s impossible right? " . format ( num ) )
2016-10-16 08:43:45 +13:00
return
2016-07-16 09:39:26 +12:00
2017-03-08 11:35:30 +13:00
nums = [ random . SystemRandom ( ) . randint ( 1 , num ) for _ in range ( 0 , int ( dice ) ) ]
2017-06-29 10:43:28 +12:00
subtotal = total = sum ( nums )
# After totalling, if we have add/subtract seperately, apply them
if add :
add = int ( add . group ( 1 ) )
total + = add
if subtract :
subtract = int ( subtract . group ( 1 ) )
total - = subtract
2017-03-06 15:49:56 +13:00
value_str = " , " . join ( " {} " . format ( x ) for x in nums )
2016-07-11 09:18:30 +12:00
2016-07-25 03:34:04 +12:00
if dice == 1 :
2018-09-24 07:23:27 +12:00
fmt = ' {0.message.author.name} has rolled a {1} sided die and got the number {2} ! ' . format (
ctx , num , value_str
)
2017-06-29 10:43:28 +12:00
if add or subtract :
fmt + = " \n Total: {} ( {} " . format ( total , subtotal )
if add :
fmt + = " + {} " . format ( add )
if subtract :
fmt + = " - {} " . format ( subtract )
fmt + = " ) "
2016-07-11 01:58:03 +12:00
else :
2018-09-24 07:23:27 +12:00
fmt = ' {0.message.author.name} has rolled {1} , {2} sided dice and got the numbers {3} ! ' . format (
ctx , dice , num , value_str
)
2017-06-29 10:43:28 +12:00
if add or subtract :
fmt + = " \n Total: {} ( {} " . format ( total , subtotal )
if add :
fmt + = " + {} " . format ( add )
if subtract :
fmt + = " - {} " . format ( subtract )
fmt + = " ) "
else :
fmt + = " \n Total: {} " . format ( total )
await ctx . send ( fmt )
2016-07-09 13:27:19 +12:00
2016-07-13 03:58:57 +12:00
2016-07-09 13:27:19 +12:00
def setup ( bot ) :
2017-05-01 11:56:02 +12:00
bot . add_cog ( Miscallaneous ( bot ) )