diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..3fc3828
Binary files /dev/null and b/.DS_Store differ
diff --git a/README.md b/README.md
index 6b0ff9b..aadc5a8 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,19 @@
[](http://gitlab.rickelobe.com/Bots/r0b3BOT/commits/master)
-# Screenshot
-
-
# Description
-This is R0b3's Discord Bot. It's creation was basically a challenge posed a coworker who said "Hey, you can write Discord Bots in Python... Make one to display your 3D printer status!".
+This is R0b3's Discord Bot. It's creation was basically a challenge posed a coworker who said "Hey, you can write Discord Bots in Python... Make one to display your 3D printer status!". But as time went on, a few more features were added. Aside from being able to query and monitor your 3D printer, it can also fetch the current status of a service monitroed by your [StatPing](https://github.com/statping/statping) instance. In addition to just fetching the current status of a service, you can subscribe to receive alerts for any StatPing service state change. All subscriptions are persistent and will remain even after the bot is restarted.
+
+# Features
+* Query & Monitor 3D Printer Status using `$printstat` command(Requires that your printer is connected to [Octoprint](https://octoprint.org/))
+
+* Query serivces states on StatPing using the `$spstatus`
+* Use `$spsub` to "Subscribe" to service change alerts for StatPing services. Subscribed alerts are persistent and are not lost on bot restart.
+
+* Play a selection of sound clips to voice channel members using the various commands (`$torololo`, `$oops`, `$promoted`, etc)
+
+# Commands
+
# Requirements
1. discord.py version 1.2.4 or greater
@@ -14,6 +22,6 @@ This is R0b3's Discord Bot. It's creation was basically a challenge posed a cow
# Configuration
1. Create a Discord Bot via Developer tools
-2. Enter various configuration parameters in bot_config.conf.example
+2. Enter configuration parameters in bot_config.conf.example
3. Enter OctoPrint config parameters in octoapi.conf.example
4. Rename *.example files as *.conf
\ No newline at end of file
diff --git a/bot_config.conf.example b/bot_config.conf.example
index 7d85f35..af77115 100644
--- a/bot_config.conf.example
+++ b/bot_config.conf.example
@@ -3,14 +3,19 @@
# Replace the placeholders with your information. Do not use quotes.
#
# discord_auth_token - this is the bot token generated via the Discord development console.
+# bot_command_prefix - The prefix used identify commands for this bot
# hass_url - URL to your home assistant instance. This is used to turn on the light by your 3d printer
# hass_token - Long Lived Access Token used to authenticate with the REST API for your Home assistant instance
# hass_light - Entity name of the light you want to control
# octoprint_ip_address - IP Address of you OctoPrint (or OctoPi) host. Used to get 3D printer stats.
+# statping_url - Server hostname of the StatPing instance we will be querying w/ the REST API
+# statping_api_key - API Key for Statping to query for service status
#
discord_auth_token =
+bot_command_prefix =
hass_url =
hass_token =
hass_light =
octoprint_ip_address =
-
+statping_url =
+statping_api_key =
\ No newline at end of file
diff --git a/images/Service_alert.png b/images/Service_alert.png
new file mode 100644
index 0000000..e83d3f7
Binary files /dev/null and b/images/Service_alert.png differ
diff --git a/images/command_list.png b/images/command_list.png
new file mode 100644
index 0000000..ea73555
Binary files /dev/null and b/images/command_list.png differ
diff --git a/r0b3bot.py b/r0b3bot.py
index 40ac5ff..654604a 100644
--- a/r0b3bot.py
+++ b/r0b3bot.py
@@ -16,24 +16,30 @@ from discord.utils import get
from discord.voice_client import VoiceClient
import requests
import asyncio
+#from aiofile import AIOFile
+import aiofiles as aiof
+import pickle
-bot = commands.Bot(command_prefix='$', description='A derpy derp of a bot.')
#Record the time the bot started
-start_time = time.time()
+start_time = time.time()
config = configparser.RawConfigParser()
configFilePath = r'bot_config.conf'
# Last error message variable used by $last_error command to display in discord
global last_error
-last_error = "No errors been recorded."
+last_error = "No errors have been recorded."
try:
config.read(configFilePath)
except:
print("Error loading config! Please check config file 'bot-config.conf'")
+#
+# Read in bot configurations
+#
+
# Setup HomeAssistant URL and Headers
HASS_TOKEN = config.get('bot-config', 'hass_token')
HASS_URL = config.get('bot-config', 'hass_url')
@@ -45,14 +51,27 @@ hassHEADERS = {
}
OCTOPRINT_IP_ADDRESS = config.get('bot-config', 'octoprint_ip_address')
-
DISCORD_AUTH_TOKEN = config.get('bot-config', 'discord_auth_token')
+BOT_COMMAND_PREFIX = config.get('bot-config', 'bot_command_prefix')
+STATPING_URL = config.get('bot-config', 'statping_url')
+STATPING_API_KEY = config.get('bot-config', 'statping_api_key')
+statpingURL = STATPING_URL + "/api/services"
+statpingHEADERS = {
+ 'Authorization': f"Bearer {STATPING_API_KEY}",
+ 'content-type': 'application/json',
+}
+
+# Configure bot
+bot = commands.Bot(command_prefix=BOT_COMMAND_PREFIX, description='A derpy derp of a bot.')
# Printing configuration details to console
print(f"HomeAssistant URL: {hassURL}")
print(f"HomeAssistant URL HEADERS: {hassHEADERS}")
print(f"HomeAssistant Light: {HASS_LIGHT}")
print(f"OctoPrint IP Address: {OCTOPRINT_IP_ADDRESS}")
+print(f"StatPing Server: {STATPING_URL}")
+print(f"StatPing API Key: {STATPING_API_KEY}")
+print(f"StatPing URL Headers: {statpingHEADERS}")
# On Ready
@bot.event
@@ -61,10 +80,21 @@ async def on_ready():
print(bot.user.name)
print(bot.user.id)
print('------')
- print("Setting activity to 'Listenting to your commands")
- activity = discord.Activity(name="your commands",type=discord.ActivityType.listening)
+ print("Setting activity to 'Listenting to your commands'")
+ activity = discord.Activity(name="your $commands",type=discord.ActivityType.listening)
await bot.change_presence(activity=activity)
+
+ # If 15 seconds or less have passed since the program was started, start the monitor.
+ runtime = time.time() - start_time
+ if runtime <= 15:
+ #Run StatPing_Monitor
+ print("Starting StatPing Monitor.")
+ await StatPing_Monitor()
+ else:
+ print(f">> Bot has been running for more than {int(runtime)} seconds. Not restarting monitor.")
+
+
@bot.command()
async def greetings(ctx):
await ctx.send(":smiley: :wave: Hello there!")
@@ -72,6 +102,7 @@ async def greetings(ctx):
@bot.event
async def on_command_error(ctx, error):
+ global last_error
if isinstance(error, commands.CommandOnCooldown):
seconds = round(error.retry_after,1)
print(f" - Error, user tried to use command while in cooldown (wait is: {seconds}")
@@ -93,21 +124,6 @@ async def on_command_error(ctx, error):
async def last_error(ctx):
await ctx.send(f"Last error recorded: {last_error}")
-@bot.command()
-async def uptime(ctx):
- # Displays how long the bot has been online
- second = time.time() - start_time
- minute, second = divmod(second, 60)
- hour, minute = divmod(minute, 60)
- day, hour = divmod(hour, 24)
- week, day = divmod(day, 7)
- week = int(week)
- day = int(day)
- hour = int(hour)
- minute = int(minute)
- second = int(second)
- await ctx.send(f"Uptime [WW:DD:HH:MM:SS]: {week:02d}:{day:02d}:{hour:02d}:{minute:02d}:{second:02d}")
-
#
# Giphy Commands
#
@@ -200,6 +216,16 @@ async def aww(ctx, member : discord.Member="NONE"):
async def eia(ctx, member : discord.Member="NONE"):
await play_sound(ctx, member, "./sounds/awesome.mp3", "$eia", 11)
+@bot.command()
+@commands.cooldown(rate=1, per=10.0, type=commands.BucketType.user)
+async def yaw(ctx, member : discord.Member="NONE"):
+ await play_sound(ctx, member, "./sounds/yaw.mp3", "$yaw", 6)
+
+@bot.command()
+@commands.cooldown(rate=1, per=10.0, type=commands.BucketType.user)
+async def heavy(ctx, member : discord.Member="NONE"):
+ await play_sound(ctx, member, "./sounds/heavy.mp3", "$heavy", 6)
+
@bot.command()
@commands.cooldown(rate=1, per=10.0, type=commands.BucketType.user)
async def rs(ctx, member : discord.Member="NONE"):
@@ -511,34 +537,404 @@ async def info(ctx):
# give users a link to invite thsi bot to their server
embed.add_field(name="Invite", value="[Invite link](https://discordapp.com/api/oauth2/authorize?client_id=416312716677087233&permissions=51264&scope=bot)")
+ # Bot Uptime
+ second = time.time() - start_time
+ minute, second = divmod(second, 60)
+ hour, minute = divmod(minute, 60)
+ day, hour = divmod(hour, 24)
+ week, day = divmod(day, 7)
+ week = int(week)
+ day = int(day)
+ hour = int(hour)
+ minute = int(minute)
+ second = int(second)
+ embed.add_field(name="Uptime [WW:DD:HH:MM:SS]:", value=f"{week:02d}:{day:02d}:{hour:02d}:{minute:02d}:{second:02d}", inline=False)
+
+ print("Getting git describe data to display as version in info embed")
+ bot_version = await run('git describe')
+ embed.add_field(name="Version:", value=f"{bot_version}", inline=False)
+
+ # Gitlab link to source
+ embed.add_field(name="Source Code:", value="[R0b3Bot on Gitlab](https://gitlab.rickelobe.com/Bots/r0b3BOT)", inline=False)
await ctx.send(embed=embed)
bot.remove_command('help')
+@bot.command()
+async def spstatus(ctx, service = "NONE"):
+#async def spalert(ctx, cmd = "NONE", arg = "NONE"):
+
+ # if cmd != "NONE":
+ # if cmd == "add":
+ # spalert +=
+ if service != "NONE":
+
+ #await get_stp_status(ctx, service.lower())
+ result = await get_stp_status(service.lower())
+
+ if result == "service not found":
+ await ctx.send(f"'{service}' was not found")
+ elif result['online']:
+ await ctx.send(f"{result['name']} is Online")
+ elif not result['online']:
+ await ctx.send(f"{result['name']} is Offline")
+ else:
+ await ctx.send(f"Received an unexpected result '{result}' querying '{service}'")
+
+ else:
+ await ctx.send("Please speficy a service name to query.")
+
+
+
+@bot.command()
+async def spsub(ctx, service = "NONE"):
+
+ spsublist = {} # Statping Subscription list to be read in from file
+ # Structure of spsublist nested dictionary
+ # spsublist[service name][list of channel ids][a state value]
+ # dict = {'Plex': {'state' : 'online', 'channels' : ['1232', '43234']}
+ # 'Space Eingineers' : {'state' : online', 'channels' : ['2343', '54563']}}
+ #
+ currentsub_request = [] # list of elements detailing the current sub request
+ datestring = datetime.now()
+ datestring = datestring.strftime("%m/%d/%Y-%H:%M:%S")
+
+ # replace _ with sapces
+ service = service.replace("_"," ")
+
+ print(f"[{datestring}]: {ctx.message.author.display_name} called '$spsub '{service}'''")
+
+ # Only work on this if the user has supplied a service name to monitor, a value of NONE
+ # means nothing was specified
+ if service != "NONE" and service != "-list" and not service.startswith('-del '):
+
+ print(f">> Querying status of '{service}' to see if it exists")
+ service_state = await get_stp_status(service)
+
+ if service != "NONE":
+
+ if service == "-list":
+ # List the serivces that this channel is subscribed to
+ # Read in spsublist
+ sublist = ''
+ async with aiof.open('spsublist.dat', 'rb') as datafile:
+ pickled_spsublist = await datafile.read()
+ spsublist = pickle.loads(pickled_spsublist)
+
+ for service in spsublist.keys():
+
+ for channel in spsublist[service]['channels']:
+ if channel == ctx.channel.id:
+ sublist += f"'{service}' "
+
+ await ctx.send(f"This channel is subscribed to: {sublist}")
+
+ elif service.startswith('-del:'):
+ # Delete the service from this channels subscriptions
+ service_toremove = service[5:]
+ print(f"{service_toremove}")
+
+ # Open the data file for read in
+ async with aiof.open('spsublist.dat', 'rb') as datafile:
+ pickled_spsublist = await datafile.read()
+ spsublist = pickle.loads(pickled_spsublist)
+
+ # We haven't found either the channel or servic; mark as false
+ found_service = False
+ found_channel = False
+
+ # Iterate through the service names in the data
+ for service in spsublist.keys():
+
+ # If the service we want to unsub from is the same as the service in the data, we need to search for the channel
+ if service.lower() == service_toremove.lower():
+
+ # We've found the service match, mark as true
+ found_service = True
+
+ # Iterate through the channels that are subbed to this service
+ for channel in spsublist[service]['channels']:
+
+ # If a channel listed in the service matches the current users channel id, we've found our match
+ if channel == ctx.channel.id:
+
+ # Marking channel as found
+ found_channel = True
+ # Get the proper name of the service
+ service_toremove = service
+ #Remove the current users channel from this service
+ spsublist[service]['channels'].remove(ctx.channel.id)
+ # Break since we're done.
+ break
+
+ # If we got to the point of finding the channel, we're done.
+ if found_channel:
+ break
+
+ if found_service and found_channel:
+ print(">> Saving dictionary to spsublist.dat")
+ async with aiof.open('spsublist.dat', 'wb') as datafile:
+ pickled_spsublist = pickle.dumps(spsublist, protocol=4)
+ await datafile.write(pickled_spsublist)
+ #await datafile.fsync()
+ await datafile.flush()
+ await ctx.send(f"This channel is unsubscribed from '{service}' alerts.")
+ else:
+ await ctx.send(f"This channel is not subscribed to alerts for '{service_toremove}''")
+
+ # If the service exists, proceed with adding it to the list.
+ elif service_state != "service not found":
+
+ print(f">> '{service}' is a valid service on StatPing.")
+ print(f">> Checking if it has already been subscribed to")
+ #await ctx.send(f"'{service_state['name']}' added to monitored services")
+ currentsub_request.append(ctx.channel.id)
+ currentsub_request.append(service_state['name'])
+ currentsub_request.append("online")
+ print(f">> Channel ID: {currentsub_request[0]}")
+ print(f">> Service Name: {currentsub_request[1]}")
+ print(f">> Initial State to set: {currentsub_request[2]}")
+
+ # read in data file containing dict of subscriptions
+ print(">> Checking for spsublist.dat")
+ if os.path.exists('spsublist.dat'):
+
+ print(">> Data file exists, reading it in.")
+ # Read in spsublist
+ async with aiof.open('spsublist.dat', 'rb') as datafile:
+ pickled_spsublist = await datafile.read()
+ spsublist = pickle.loads(pickled_spsublist)
+
+ print(f">> Checking if serivce is already in datafile")
+ found_service = False
+ found_channel = False
+ for service in spsublist.keys():
+ print(f">> Does {service.lower()} == {currentsub_request[1].lower()}")
+
+ if service.lower() == currentsub_request[1].lower():
+ found_service = True
+ # This service matched what the user is trying to subscribe to
+ # Now we need to check if this is for the same channel
+ print(f">> Found {service} in data file, checking for channel")
+
+ for channel in spsublist[service]['channels']:
+ if channel == currentsub_request[0]:
+ print(f">> This channel is already subscribed to {service}")
+ await ctx.send(f"This channel is already subscribed to {service} alerts")
+ found_channel = True
+ break
+ if not found_channel:
+ # Append the current channel id to the list for this service
+ print(f">> Adding serivce '{currentsub_request[1]}' to {ctx.channel.id}")
+ await ctx.send(f"{service} has been added to monitored services for this channel")
+ spsublist[service]['channels'].append(currentsub_request[0])
+ break
+
+ if not found_service:
+ # Append the current channel id to the list for this service
+ print(f">> Adding serivce '{currentsub_request[1]}' to {ctx.channel.id}")
+ await ctx.send(f"{currentsub_request[1]} has been added to monitored services for this channel")
+ #newsub = {}
+ #newsub[currentsub_request[1]] = {'state' : 'online', 'channels' : [currentsub_request[0]]}
+ spsublist[currentsub_request[1]] = {'state' : 'online', 'channels' : [currentsub_request[0]]}
+
+ else:
+
+ print(">> spsublist.dat does not exist, new file will be created")
+
+ # append new subscription to dict
+ print(">> Creating new dictionary")
+ spsublist[currentsub_request[1]] = {'state' : 'online', 'channels' : [currentsub_request[0]]}
+ print(f">> {service} subscription has been saved")
+ await ctx.send(f"{service} has been added to monitored services for this channel")
+
+ #Write updated array to data file
+ print(">> Saving dictionary to spsublist.dat")
+ async with aiof.open('spsublist.dat', 'wb') as datafile:
+ pickled_spsublist = pickle.dumps(spsublist, protocol=4)
+ await datafile.write(pickled_spsublist)
+ #await datafile.fsync()
+ await datafile.flush()
+
+ print(">> Done")
+ #await ctx.send(f"'{service_state['name']}' added to monitored services")
+
+ else:
+ print(f">> '{service}' does not exist, cancelling subscription")
+ await ctx.send(f"{service} was not found")
+
+async def StatPing_Monitor():
+
+ # reads in "spsublist.dat" every 60 seconds then iterates
+ # through to check for service status changes. This function will need to check
+ # that "spsublist.dat" exists and is not empty. This function should be called
+ # in bot.on_ready
+
+ #new_spsublist = {}
+
+ # read in data file containing list of subscriptions
+ while True:
+ print("Statping Monitor: Reading in spsublist.dat")
+ if os.path.exists('spsublist.dat'):
+ async with aiof.open('spsublist.dat', 'rb') as datafile:
+ pickled_spsublist = await datafile.read()
+ spsublist = pickle.loads(pickled_spsublist)
+
+ for service in spsublist.keys():
+ # service used to be subscription
+ status = await get_stp_status(service)
+ print(f">> {service} Online: {status['online']} | Previous state: {spsublist[service]['state']}")
+ if status != 'status not found':
+ if status['online'] and spsublist[service]['state'] == 'online':
+
+ # nothing has changed, no alert needed
+ await asyncio.sleep(1)
+ elif not status['online'] and spsublist[service]['state'] == "offline":
+
+ # again, nothing has changed no alert needed
+ await asyncio.sleep(1)
+ else:
+
+ print(f">> Status of '{service}' has changed, notifying subscribed channels")
+
+ if status['online']:
+ spsublist[service]['state'] = 'online'
+ for channel in spsublist[service]['channels']:
+ ctx = bot.get_channel(channel)
+ print(f">> Alerting {ctx} that {service} is Online")
+ embed = discord.Embed(title=f"Service Alert", description=f"{service} is Online!", color=0x00ff40)
+ await ctx.send(embed=embed)
+
+ elif not status['online']:
+ spsublist[service]['state'] = 'offline'
+ for channel in spsublist[service]['channels']:
+ ctx = bot.get_channel(channel)
+ print(f">> Alerting {ctx} that {service} is Offline")
+ embed = discord.Embed(title=f"Service Alert", description=f"{service} is Offline!", color=0xff2200)
+ await ctx.send(embed=embed)
+
+ else:
+ for channel in spsublist[service]['channels']:
+ ctx = bot.get_channel(channel)
+ print(f">> Alerting {ctx} that {service} is an Unknown state")
+ embed = discord.Embed(title=f"Service Alert", description=f"{service} is in an unknown state!", color=0xffff00)
+ await ctx.send(embed=embed)
+ print(">> Done")
+
+ #new_spsublist.append(subscription)
+
+ # Write status changes to spsublist.dat
+ async with aiof.open('spsublist.dat', 'wb') as datafile:
+ pickled_spsublist = pickle.dumps(spsublist)
+ await datafile.write(pickled_spsublist)
+ #await datafile.fsync()
+ await datafile.flush()
+
+ else:
+ print(">> spsublist.dat does not exist, nothing to do.")
+
+ await asyncio.sleep(60)
+
+
+async def sp_monitor(sublist):
+
+ # Checks the services listed in spsublist for status change
+ # Sublist is a list of 3 items:
+ # - ctx (used to send alert to channel)
+ # - service (the name of the service to check)
+ # - status string (last status of the service, either 'online' or 'offline')
+
+ ctx = sublist[0]
+
+
+ while True:
+
+ status = await get_stp_status(spsublist[1])
+ if status['online'] and spsublist[2] == "online":
+ # nothing has changed, no alert needed
+ await asyncio.sleep(1)
+ elif not status['online'] and spsublist[2] == "offline":
+ # again, nothing has changed no alert needed
+ await asyncio.sleep(1)
+ else:
+ print(f"Status of {status['name']}' has changed")
+ if status['online']:
+ embed = discord.Embed(title=f"{status['name']} Service Alert", description=f"{spsublist[1]} is Online!", color=0x00ff40)
+ await ctx.send(embed=embed)
+ #await ctx.send(f"{spsublist[1]} is Online!")
+ spsublist[2] = "online"
+ elif not status['online']:
+ embed = discord.Embed(title=f"{status['name']} Service Alert", description=f"{spsublist[1]} is Offline!", color=0xff2200)
+ await ctx.send(embed=embed)
+ #await ctx.send(f"{spsublist[1]} is Offline!")
+ spsublist[2] = "offline"
+ else:
+ embed = discord.Embed(title=f"{status['name']} Service Alert", description=f"{spsublist[1]} is in an unknown state!", color=0xffff00)
+ await ctx.send(embed=embed)
+ #await ctx.send(f"Unknown state for service {spsublist[1]}")
+ await asyncio.sleep(60)
+
+async def get_stp_status(service):
+
+ # Get StatPing status via REST API
+ spservice_array = requests.get(statpingURL, headers=statpingHEADERS)
+ found = False
+
+ # spservice_array is json data, need to treat it as such
+ for spservice in spservice_array.json():
+
+ # using lower() to eliminate any case mismatch problems
+ if spservice['name'].lower() == service.lower():
+
+ found = True
+ return spservice
+
+ # Want to alert user if the service was not found
+ if not found:
+ return "service not found"
+
@bot.command()
async def help(ctx):
embed = discord.Embed(title="R0b3BOT", description="List of commands are:", color=0xeee657)
- embed.add_field(name="$printstat", value="Uploads a snapshot of Rich's 3D printer and current stats", inline=False)
- embed.add_field(name="$explain", value="Displays Dalek EXPLAIN gif", inline=False)
- embed.add_field(name="$holyshit", value="Displays Marty McFly HOLY SHIT gif", inline=False)
- embed.add_field(name="$greetings", value="Gives a nice greet message", inline=False)
- embed.add_field(name="$cat", value="Gives a cute cat gif to lighten up the mood.", inline=False)
- embed.add_field(name="$bitch @member", value="Plays clip of BigEric420 saying 'maybe you shouldn't be such a bitch.mp3'", inline=False)
- embed.add_field(name="$boom @member", value="Plays 'Boom Bitch' sound clip", inline=False)
- embed.add_field(name="$cowbell @member", value="Plays 'SNL More Cowbell' sound clip", inline=False)
- embed.add_field(name="$oops @member", value="Plays 'Oops.mp3'", inline=False)
- embed.add_field(name="$promoted @member", value="Plays Battlefield Friends 'PROMOTED!' sound clip", inline=False)
- embed.add_field(name="$trololo @member", value="Plays a clip of Trololo song", inline=False)
- embed.add_field(name="$leeroy @member", value="Plays Leeeerrroooyyy Jenkins clip", inline=False)
- embed.add_field(name="$eia @member", value="Plays 'Everything is Awesome' song clip")
- embed.add_field(name="$info", value="Gives a little info about the bot", inline=False)
- embed.add_field(name="$help", value="Gives this message", inline=False)
- embed.add_field(name="$last_error", value="Will display the real error message the bot has last encountered for additional debugging info")
- embed.add_field(name="$uptime", value="Displays how long the bot has been up since last service restart")
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}printstat", value="Uploads a snapshot of Rich's 3D printer and current stats", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}explain", value="Displays Dalek EXPLAIN gif", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}holyshit", value="Displays Marty McFly HOLY SHIT gif", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}greetings", value="Gives a nice greet message", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}cat", value="Gives a cute cat gif to lighten up the mood.", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}bitch @member", value="Plays clip of BigEric420 saying 'maybe you shouldn't be such a bitch.mp3'", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}boom @member", value="Plays 'Boom Bitch' sound clip", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}cowbell @member", value="Plays 'SNL More Cowbell' sound clip", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}oops @member", value="Plays 'Oops.mp3'", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}promoted @member", value="Plays Battlefield Friends 'PROMOTED!' sound clip", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}trololo @member", value="Plays a clip of Trololo song", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}leeroy @member", value="Plays Leeeerrroooyyy Jenkins clip", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}eia @member", value="Plays 'Everything is Awesome' song clip")
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}info", value="Gives a little info about the bot", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}help", value="Gives this message", inline=False)
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}last_error", value="Will display the real error message the bot has last encountered for additional debugging info")
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}spstatus servicename", value="Retrieves status of service from StatPing")
+ embed.add_field(name=f"{BOT_COMMAND_PREFIX}spsub service", value="Allows user to subscribe to service status change alerts for the channel. Can replace with -list and -del:service to manage alerts. NOTICE:Replace spaces in service names w/ '_'!")
await ctx.send(embed=embed)
- embed = discord.Embed(title="A note about sound clips:", description="Sound clips are only played voice channels. If no user is specified when calling the command, the sound will played in the channel the user is currently joined to. When a username is specified, the bot will play the sound in the channel that user is currently in.")
+ embed = discord.Embed(title="A note about sound clips:", description="Sound clips are only played in voice channels. If a user is not specified when calling the command, the sound will played in the channel of that issuing user is currently joined to. When a username is specified, the bot will play the sound in the channel that user is currently in.")
await ctx.send(embed=embed)
+# Function to run shell command and return stdout or sterr
+async def run(cmd):
+ proc = await asyncio.create_subprocess_shell(
+ cmd,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE)
+
+ stdout, stderr = await proc.communicate()
+
+ print(f'[{cmd!r} exited with {proc.returncode}]')
+ if stdout:
+ #print(f'[stdout]\n{stdout.decode()}')
+ return stdout.decode()
+ if stderr:
+ #print(f'[stderr]\n{stderr.decode()}')
+ return stderr.decode()
+
bot.run(DISCORD_AUTH_TOKEN)
diff --git a/sounds/heavy.mp3 b/sounds/heavy.mp3
new file mode 100644
index 0000000..00d1eb6
Binary files /dev/null and b/sounds/heavy.mp3 differ
diff --git a/sounds/yaw.mp3 b/sounds/yaw.mp3
new file mode 100644
index 0000000..cc6f08a
Binary files /dev/null and b/sounds/yaw.mp3 differ