From 77e5b84c398f3479339bca4f1faa917391401ce0 Mon Sep 17 00:00:00 2001 From: Dan Hess Date: Thu, 7 Jul 2016 08:05:42 -0500 Subject: [PATCH 1/3] Add files via upload --- bot.py | 596 ++++++++++++++++++++++++++++++++++++++++++++++++++++ playlist.py | 252 ++++++++++++++++++++++ 2 files changed, 848 insertions(+) create mode 100644 bot.py create mode 100644 playlist.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..ddddc80 --- /dev/null +++ b/bot.py @@ -0,0 +1,596 @@ +#!/usr/local/bin/python3.5 + +import asyncio +import discord +from discord.ext import commands +import sys +import os +import random +import subprocess +import urllib.request +import urllib.parse +import json +import re +import pymysql.cursors +import yaml +import playlist +from threading import Timer + +with open("/home/phxntx5/public_html/bot/config.yml","r") as f: + global_config = yaml.load(f) + +botDescription = global_config.get("description") +commandPrefix = global_config.get("command_prefix") + +#Custom predicates +def isOwner(): + def predicate(ctx): + return ctx.message.author.id==ownerID + return commands.check(predicate) +def isMod(): + def predicate(ctx): + return ctx.message.author.top_role.permissions.kick_members + return commands.check(predicate) +def isAdmin(): + def predicate(ctx): + return ctx.message.author.top_role.permissions.manage_server + return commands.check(predicate) +def isPM(): + def predicate(ctx): + return ctx.message.channel.is_private + return commands.check(predicate) +def battled(): + def predicate(ctx): + return ctx.message.author==battleP2 + return commands.check(predicate) + +bot = commands.Bot(command_prefix=commandPrefix,description=botDescription) +music = playlist.Music(bot) +bot.add_cog(music) + +#Turn battling off, reset users +def battlingOff(): + global battleP1 + global battleP2 + global battling + battling = False + battleP1 = "" + battleP2 = "" + +#Bot event overrides +@bot.event +async def on_ready(): + #Change the status upon connection to the default status + game = discord.Game(name=defaultStatus,type=0) + await bot.change_status(game) + cursor = connection.cursor() + + '''success = checkSetup(cursor) + if success=="Error: default_db": + await bot-send_message(determineId(ownerID),("The bot ran into an error while checking the database information." + "Please double check the config.yml file, make sure the database is created, and the user has access to the database"))''' + + cursor.execute('use {0}'.format(db_default)) + cursor.execute('select channel_id from restart_server where id=1') + result = cursor.fetchone()['channel_id'] + if int(result)!=0: + await bot.send_message(determineId(result),"I have just finished restarting!") + cursor.execute('update restart_server set channel_id=0 where id=1') + connection.commit() + +@bot.event +async def on_member_join(member): + await bot.say("Welcome to the '{0.server.name}' server {0.mention}!".format(member)) + +@bot.event +async def on_member_remove(member): + await bot.say("{0} has left the server, I hope it wasn't because of something I said :c".format(member)) + +#Bot commands +@bot.command() +async def joke(): + try: + fortuneCommand = "/usr/bin/fortune riddles" + fortune = subprocess.check_output(fortuneCommand.split()).decode("utf-8") + await bot.say(fortune) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +@isOwner() +async def restart(ctx): + try: + cursor = connection.cursor() + cursor.execute('use {0}'.format(db_default)) + sql = "update restart_server set channel_id={0} where id=1".format(ctx.message.channel.id) + cursor.execute(sql) + connection.commit() + await bot.say("Restarting; see you in the next life {0}!".format(ctx.message.author.mention)) + python = sys.executable + os.execl(python, python, *sys.argv) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +@isOwner() +async def py(ctx): + try: + match_single = getter.findall(ctx.message.content) + match_multi = multi.findall(ctx.message.content) + if not match_single and not match_multi: + return + else: + if not match_multi: + result = eval(match_single[0]) + await bot.say("```{0}```".format(result)) + else: + def r(v): + loop.create_task(bot.say("```{0}```".format(v))) + exec(match_multi[0]) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +@isOwner() +async def shutdown(ctx): + try: + fmt = 'Shutting down, I will miss you {0.author.name}' + await bot.say(fmt.format(ctx.message)) + await bot.logout() + await bot.close() + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command() +@isOwner() +async def avatar(content): + try: + file = '/home/phxntx5/public_html/bot/images/' + content + with open(file, 'rb') as fp: + await bot.edit_profile(avatar=fp.read()) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command() +@isOwner() +async def name(newNick): + try: + await bot.edit_profile(username=newNick) + await bot.say('Changed username to ' + newNick) + # Restart the bot after this, as profile changes are not immediate + python = sys.executable + os.execl(python, python, *sys.argv) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True,no_pm=True) +@isOwner() +async def leave(ctx): + try: + await bot.say('Why must I leave? Hopefully I can come back :c') + await bot.leave_server(ctx.message.server) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command() +@isMod() +async def status(*stat : str): + try: + newStatus = ' '.join(stat) + game = discord.Game(name=newStatus, type=0) + await bot.change_status(game) + await bot.say("Just changed my status to '{0}'!".format(newStatus)) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +@isMod() +async def say(ctx,*msg : str): + try: + msg = ' '.join(msg) + await bot.say(msg) + await bot.delete_message(ctx.message) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command() +async def urban(*msg : str): + try: + term = '+'.join(msg) + url = "http://api.urbandictionary.com/v0/define?term={}".format(term) + response = urllib.request.urlopen(url) + data = json.loads(response.read().decode('utf-8')) + if len(data['list'])==0: + await bot.say("No result with that term!") + else: + await bot.say(data['list'][0]['definition']) + except discord.HTTPException: + await bot.say('```Error: Defintion is too long for me to send```') + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +async def derpi(ctx,*search : str): + try: + if len(search) > 0: + url = 'https://derpibooru.org/search.json?q=' + query = '+'.join(search) + url += query + if ctx.message.channel.id in nsfwChannels: + url+=",+explicit&filter_id=95938" + # url should now be in the form of url?q=search+terms + # Next part processes the json format, and saves the data in useful lists/dictionaries + response = urllib.request.urlopen(url) + data = json.loads(response.read().decode('utf-8')) + results = data['search'] + + if len(results) > 0: + index = random.randint(0, len(results) - 1) + randImageUrl = results[index].get('representations').get('full')[2:] + randImageUrl = 'http://' + randImageUrl + imageLink = randImageUrl.strip() + else: + await bot.say("No results with that search term, {0}!".format(ctx.message.author.mention)) + return + else: + with urllib.request.urlopen('https://derpibooru.org/images/random') as response: + imageLink = response.geturl() + url = 'https://shpro.link/redirect.php/' + data = urllib.parse.urlencode({'link': imageLink}).encode('ascii') + response = urllib.request.urlopen(url, data).read().decode('utf-8') + await bot.say(response) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +async def roll(ctx): + try: + num = random.randint(1, 6) + fmt = '{0.author.name} has rolled a die and got the number {1}!' + await bot.say(fmt.format(ctx.message, num)) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(no_pm=True) +@battled() +async def accept(): + try: + if not battling: + return + num = random.randint(1, 100) + fmt = battleWins[random.randint(0, len(battleWins) - 1)] + if num <= 50: + await bot.say(fmt.format(battleP1.mention, battleP2.mention)) + updateBattleRecords(battleP1,battleP2) + elif num > 50: + await bot.say(fmt.format(battleP2.mention, battleP1.mention)) + updateBattleRecords(battleP2,battleP1) + battlingOff() + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(no_pm=True) +@battled() +async def decline(): + try: + if not battling: + return + await bot.say("{0} has chickened out! {1} wins by default!".format(battleP2.mention, battleP1.mention)) + updateBattleRecords(battleP1,battleP2) + battlingOff() + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True) +async def commands(ctx): + try: + fmt = 'Hello {0.author.name}!\n\nThis is {1.user.name}! ' + fmt += 'Here is a list of commands that you have access to ' + fmt += '(please note that this only includes the commands that you have access to):' + await bot.whisper(fmt.format(ctx.message, bot)) + + fmt2 = 'I have just sent you a private message, containing all the commands you have access to {0.author.mention}!' + ocmds = 'Commands for everyone: ```' + for com, act in openCommands.items(): + ocmds += commandPrefix + com + ": " + act + "\n\n" + ocmds += '```' + await bot.whisper(ocmds) + vcmds = 'Voice Commands: ```' + for com, act in voiceCommands.items(): + vcmds += commandPrefix + com + ": " + act + "\n\n" + vcmds += '```' + await bot.whisper(vcmds) + if ctx.message.author.top_role.permissions.kick_members: + if len(modCommands) > 0: + mcmds = 'Moderator Commands: ```' + for com, act in modCommands.items(): + mcmds += commandPrefix + com + ": " + act + "\n\n" + mcmds += '```' + await bot.whisper(mcmds) + if ctx.message.author.top_role.permissions.manage_server: + if len(adminCommands) > 0: + acmds = 'Admin Commands: ```' + for com, act in adminCommands.items(): + acmds += commandPrefix + com + ": " + act + "\n\n" + acmds += '```' + await bot.whisper(acmds) + if ctx.message.author.id == ownerID: + if len(ownerCommands) > 0: + owncmds = 'Owner Commands: ```' + for com, act in ownerCommands.items(): + owncmds += commandPrefix + com + ": " + act + "\n\n" + owncmds += '```' + await bot.whisper(owncmds) + await bot.say(fmt2.format(ctx.message)) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True,no_pm=True) +async def battle(ctx): + try: + global battleP1 + global battleP2 + global battling + if battling: + return + if len(ctx.message.mentions) == 0: + await bot.say("You must mention someone in the room " + ctx.message.author.mention + "!") + return + if len(ctx.message.mentions) > 1: + await bot.say("You cannot battle more than one person at once!") + return + player2 = ctx.message.mentions[0] + if ctx.message.author.id == player2.id: + await bot.say("Why would you want to battle yourself? Suicide is not the answer") + return + if bot.user.id == player2.id: + await bot.say("I always win, don't even try it.") + return + fmt = "{0.mention} has challenged you to a battle {1.mention}\n!accept or !decline" + battleP1 = ctx.message.author + battleP2 = player2 + await bot.say(fmt.format(ctx.message.author, player2)) + t = Timer(180, battlingOff) + t.start() + battling = True + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True,no_pm=True) +async def boop(ctx): + try: + if len(ctx.message.mentions) == 0: + await bot.say("You must mention someone in the room " + ctx.message.author.mention + "!") + return + if len(ctx.message.mentions) > 1: + await bot.say("You cannot boop more than one person at once!") + return + boopee = ctx.message.mentions[0] + booper = ctx.message.author + if boopee.id == booper.id: + await bot.say("You can't boop yourself! Silly...") + return + if boopee.id == bot.user.id: + await bot.say("Why the heck are you booping me? Get away from me >:c") + return + + cursor = connection.cursor() + cursor.execute('use {0}'.format(db_boops)) + sql = "show tables like '" + str(booper.id) + "'" + cursor.execute(sql) + result = cursor.fetchone() + amount = 1 + # Booper's table exists, continue + if result is not None: + sql = "select `amount` from `" + booper.id + "` where id='" + str(boopee.id) + "'" + cursor.execute(sql) + result = cursor.fetchone() + # Boopee's entry exists, continue + if result is not None: + amount = result.get('amount') + 1 + sql = "update `" + str(booper.id) + "` set amount = " + str(amount) + " where id=" + str( + boopee.id) + cursor.execute(sql) + # Boopee does not exist, need to create the field for it + else: + sql = "insert into `" + str(booper.id) + "` (id,amount) values ('" + str(boopee.id) + "',1)" + cursor.execute(sql) + # Booper's table does not exist, need to create the table + else: + sql = "create table `" + str( + booper.id) + "` (`id` varchar(255) not null,`amount` int(11) not null,primary key (`id`)) engine=InnoDB default charset=utf8 collate=utf8_bin" + cursor.execute(sql) + sql = "insert into `" + str(booper.id) + "` (id,amount) values ('" + str(boopee.id) + "',1)" + cursor.execute(sql) + fmt = "{0.mention} has just booped you {1.mention}! That's {2} times now!" + await bot.say(fmt.format(booper, boopee, amount)) + connection.commit() + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True,no_pm=True) +async def mostboops(ctx): + try: + cursor = connection.cursor() + cursor.execute('use {0}'.format(db_boops)) + sql = "select id,amount from `{0}` where amount=(select MAX(amount) from `{0}`)".format(ctx.message.author.id) + cursor.execute(sql) + result = cursor.fetchone() + member = determineId(result.get('id')) + await bot.say("{0} you have booped {1} the most amount of times, coming in at {2} times".format(ctx.message.author.mention,member.mention,result.get('amount'))) + connection.commit() + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +@bot.command(pass_context=True,no_pm=True) +async def mostwins(ctx): + try: + members = ctx.message.server.members + cursor = connection.cursor() + cursor.execute('use {0}'.format(db_default)) + sql = "select * from battle_records" + cursor.execute(sql) + result = cursor.fetchall() + count = 0 + fmt = [] + if result is not None: + for r in result: + member = determineId(r['id']) + if member in members: + record = r['record'] + + winAmt = int(record.split('-')[0]) + loseAmt = int(record.split('-')[1]) + percentage = winAmt / ( winAmt + loseAmt ) + + position = count + + indexPercentage = 0 + if count > 0: + indexRecord = re.search('\d+-\d+',fmt[position-1]).group(0) + indexWin = int(indexRecord.split('-')[0]) + indexLose = int(indexRecord.split('-')[1]) + indexPercentage = indexWin / (indexWin + indexLose) + while position > 0 and indexPercentage < percentage: + position -= 1 + indexRecord = re.search('\d+-\d+',fmt[position-1]).group(0) + indexWin = int(indexRecord.split('-')[0]) + indexLose = int(indexRecord.split('-')[1]) + indexPercentage = indexWin / (indexWin + indexLose) + fmt.insert(position,"{0} has a battling record of {1}".format(member.name,record)) + count+=1 + for index in range(0,len(fmt)): + fmt[index] = "{0}) {1}".format(index+1,fmt[index]) + connection.commit() + if len(fmt) == 0: + await bot.say("```No battling records found from any members in this server```") + return + await bot.say("```{}```".format("\n".join(fmt))) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await bot.say(fmt.format(type(e).__name__, e)) + +def determineId(ID): + if type(ID) is int: + ID=str(ID) + member = discord.utils.find(lambda m: m.id == ID, bot.get_all_members()) + if member is not None: + return member + msg = discord.utils.find(lambda m: m.id == ID, bot.messages) + if msg is not None: + return msg + server = discord.utils.find(lambda s: s.id == ID, bot.servers) + if server is not None: + return server + channel = discord.utils.find(lambda c: c.id == ID, bot.get_all_channels()) + if channel is not None: + return channel + +def updateBattleRecords(winner,loser): + cursor = connection.cursor() + cursor.execute('use {0}'.format(db_default)) + + #Update winners records + sql = "select record from battle_records where id={0}".format(winner.id) + cursor.execute(sql) + result = cursor.fetchone() + if result is not None: + result = result['record'].split('-') + result[0] = str(int(result[0])+1) + sql = "update battle_records set record ='{0}' where id='{1}'".format("-".join(result),winner.id) + cursor.execute(sql) + else: + sql = "insert into battle_records (id,record) values ('{0}','1-0')".format(winner.id) + cursor.execute(sql) + + connection.commit() + + #Update losers records + sql = "select record from battle_records where id={0}".format(loser.id) + cursor.execute(sql) + result = cursor.fetchone() + if result is not None: + result = result['record'].split('-') + result[1] = str(int(result[1])+1) + sql = "update battle_records set record ='{0}' where id='{1}'".format('-'.join(result),loser.id) + cursor.execute(sql) + else: + sql = "insert into battle_records (id,record) values ('{0}','0-1')".format(loser.id) + cursor.execute(sql) + + connection.commit() + +def checkSetup(cursor): + try: + cursor.execute('use {}'.format(db_default)) + except pymysql.OperationalError: + return "Error: default_db" + else: + try: + cursor.execute('describe battle_records') + except pymysql.ProgrammingError: + #battle_records does not exist, create it + sql = "create table `battle_records` (`id` varchar(32) not null,`record` varchar(32) not null,primary key (`id`)) engine=InnoDB default charset=utf8 collate=utf8_bin" + cursor.execute(sql) + connection.commit() + try: + cursor.execute('describe restart_server') + except pymysql.ProgrammingError: + #restart_server does not exist, create it + sql = "create table `restart_server` (`id` int(11) not null auto_increment,`channel_id` varchar(32) not null,primary key (`id`)) engine=InnoDB default charset=utf8 collate=utf8_bin;" + cursor.execute(sql) + connection.commit() + sql = "insert into restart_server (id,channel_id) values (1,'0')" + cursor.execute(sql) + connection.commit() + try: + cursor.execute('use {}'.format(db_boops)) + except pymysql.OperationalError: + return "Error: boop_db" + +db_default = global_config.get("db_default") +db_boops = global_config.get("db_boops") +nsfwChannels = global_config.get("nsfw_channel") +connection = pymysql.connect(host=global_config.get("db_host"), user=global_config.get("db_user"), +password=global_config.get("db_user_pass"),charset='utf8mb4',cursorclass=pymysql.cursors.DictCursor) + +battling=False +battleP1=None +battleP2=None + +battleWins = global_config.get("battleWins",[]) +defaultStatus = global_config.get("default_status","") +botToken = global_config.get("bot_token","") +ownerID = global_config.get("owner_id","") + +modCommands = global_config.get("modCommands",{}) +adminCommands = global_config.get("adminCommands",{}) +openCommands = global_config.get("openCommands",{}) +ownerCommands = global_config.get("ownerCommands",{}) +voiceCommands = global_config.get("voiceCommands",{}) + +getter = re.compile(r'`(?!`)(.*?)`') +multi = re.compile(r'```(.*?)```',re.DOTALL) +loop = asyncio.get_event_loop() +try: + bot.run(botToken) +except: + quit() \ No newline at end of file diff --git a/playlist.py b/playlist.py new file mode 100644 index 0000000..5bc8981 --- /dev/null +++ b/playlist.py @@ -0,0 +1,252 @@ +import asyncio +import discord +import traceback +from discord.ext import commands + +if not discord.opus.is_loaded(): + discord.opus.load_opus('/usr/lib64/libopus.so.0') + +class VoiceEntry: + def __init__(self, message, player): + self.requester = message.author + self.channel = message.channel + self.player = player + + def __str__(self): + fmt = '*{0.title}* uploaded by {0.uploader} and requested by {1.display_name}' + duration = self.player.duration + if duration: + fmt += ' [length: {0[0]}m {0[1]}s]'.format(divmod(round(duration,0), 60)) + return fmt.format(self.player, self.requester) + +class VoiceState: + def __init__(self, bot): + self.current = None + self.voice = None + self.bot = bot + self.play_next_song = asyncio.Event() + self.songs = asyncio.Queue() + self.skip_votes = set() # a set of user_ids that voted + self.audio_player = self.bot.loop.create_task(self.audio_player_task()) + + def is_playing(self): + if self.voice is None or self.current is None: + return False + + player = self.current.player + return not player.is_done() + + @property + def player(self): + return self.current.player + + def skip(self): + self.skip_votes.clear() + if self.is_playing(): + self.player.stop() + + def toggle_next(self): + self.bot.loop.call_soon_threadsafe(self.play_next_song.set) + + async def audio_player_task(self): + while True: + self.play_next_song.clear() + self.current = await self.songs.get() + await self.bot.send_message(self.current.channel, 'Now playing ' + str(self.current)) + self.current.player.start() + await self.play_next_song.wait() + +class Music: + """Voice related commands. + Works in multiple servers at once. + """ + def __init__(self, bot): + self.bot = bot + self.voice_states = {} + + def get_voice_state(self, server): + state = self.voice_states.get(server.id) + if state is None: + state = VoiceState(self.bot) + self.voice_states[server.id] = state + + return state + + async def create_voice_client(self, channel): + voice = await self.bot.join_voice_channel(channel) + state = self.get_voice_state(channel.server) + state.voice = voice + + def __unload(self): + for state in self.voice_states.values(): + try: + state.audio_player.cancel() + if state.voice: + self.bot.loop.create_task(state.voice.disconnect()) + except: + pass + def isMod(): + def predicate(ctx): + return ctx.message.author.top_role.permissions.kick_members + return commands.check(predicate) + + @commands.command(pass_context=True, no_pm=True) + async def join(self, ctx, *, channel : discord.Channel): + """Joins a voice channel.""" + try: + await self.create_voice_client(channel) + except discord.InvalidArgument: + await self.bot.say('This is not a voice channel...') + except discord.ClientException: + await self.bot.say('Already in a voice channel...') + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await self.bot.say(fmt.format(type(e).__name__, e)) + else: + await self.bot.say('Ready to play audio in ' + channel.name) + + @commands.command(pass_context=True, no_pm=True) + async def summon(self, ctx): + """Summons the bot to join your voice channel.""" + summoned_channel = ctx.message.author.voice_channel + if summoned_channel is None: + await self.bot.say('You are not in a voice channel.') + return False + + state = self.get_voice_state(ctx.message.server) + if state.voice is None: + state.voice = await self.bot.join_voice_channel(summoned_channel) + else: + await state.voice.move_to(summoned_channel) + return True + + @commands.command(pass_context=True, no_pm=True) + async def play(self, ctx, *, song : str): + """Plays a song. + If there is a song currently in the queue, then it is + queued until the next song is done playing. + This command automatically searches as well from YouTube. + The list of supported sites can be found here: + https://rg3.github.io/youtube-dl/supportedsites.html + """ + state = self.get_voice_state(ctx.message.server) + opts = { + 'default_search': 'auto', + 'quiet': True, + } + + if state.voice is None: + success = await ctx.invoke(self.summon) + if not success: + return + + player = await state.voice.create_ytdl_player(song, ytdl_options=opts, after=state.toggle_next) + player.volume = 0.6 + entry = VoiceEntry(ctx.message, player) + await self.bot.say('Enqueued ' + str(entry)) + await state.songs.put(entry) + + @commands.command(pass_context=True, no_pm=True) + @isMod() + async def volume(self, ctx, value : int): + """Sets the volume of the currently playing song.""" + + state = self.get_voice_state(ctx.message.server) + if state.is_playing(): + player = state.player + player.volume = value / 100 + await self.bot.say('Set the volume to {:.0%}'.format(player.volume)) + + @commands.command(pass_context=True, no_pm=True) + @isMod() + async def pause(self, ctx): + """Pauses the currently played song.""" + state = self.get_voice_state(ctx.message.server) + if state.is_playing(): + player = state.player + player.pause() + + @commands.command(pass_context=True, no_pm=True) + @isMod() + async def resume(self, ctx): + """Resumes the currently played song.""" + state = self.get_voice_state(ctx.message.server) + if state.is_playing(): + player = state.player + player.resume() + + @commands.command(pass_context=True, no_pm=True) + @isMod() + async def stop(self, ctx): + """Stops playing audio and leaves the voice channel. + This also clears the queue. + """ + server = ctx.message.server + state = self.get_voice_state(server) + + if state.is_playing(): + player = state.player + player.stop() + + try: + state.audio_player.cancel() + del self.voice_states[server.id] + await state.voice.disconnect() + except: + pass + @commands.command(pass_context=True,no_pm=True) + async def queuelength(self,ctx): + """Prints the length of the queue""" + try: + await self.bot.say("There are a total of {} songs in the queue".format(str(self.get_voice_state(ctx.message.server).songs.qsize()))) + except: + await self.bot.say(traceback.format_exc()) + + @commands.command(pass_context=True, no_pm=True) + async def skip(self, ctx): + """Vote to skip a song. The song requester can automatically skip. + 3 skip votes are needed for the song to be skipped. + """ + + state = self.get_voice_state(ctx.message.server) + if not state.is_playing(): + await self.bot.say('Not playing any music right now...') + return + + voter = ctx.message.author + if voter == state.current.requester: + await self.bot.say('Requester requested skipping song...') + state.skip() + elif voter.id not in state.skip_votes: + state.skip_votes.add(voter.id) + total_votes = len(state.skip_votes) + if total_votes >= 3: + await self.bot.say('Skip vote passed, skipping song...') + state.skip() + else: + await self.bot.say('Skip vote added, currently at [{}/3]'.format(total_votes)) + else: + await self.bot.say('You have already voted to skip this song.') + + @commands.command(pass_context=True, no_pm=True) + @isMod() + async def modskip(self, ctx): + """Forces a song skip, can only be used by a moderator""" + state = self.get_voice_state(ctx.message.server) + if not state.is_playing(): + await self.bot.say('Not playing any music right now...') + return + + state.skip() + await self.bot.say('Song has just been skipped.') + + @commands.command(pass_context=True, no_pm=True) + async def playing(self, ctx): + """Shows info about the currently played song.""" + + state = self.get_voice_state(ctx.message.server) + if state.current is None: + await self.bot.say('Not playing anything.') + else: + skip_count = len(state.skip_votes) + await self.bot.say('Now playing {} [skips: {}/3]'.format(state.current, skip_count)) From 7c82ac6e23288fe8ad47462d3f94e87741522076 Mon Sep 17 00:00:00 2001 From: Dan Hess Date: Thu, 7 Jul 2016 08:05:58 -0500 Subject: [PATCH 2/3] Add files via upload --- config.yml | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 config.yml diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..96f9307 --- /dev/null +++ b/config.yml @@ -0,0 +1,64 @@ +bot_token: MTgzNzQ5MDg3MDM4OTMwOTQ0.CiNDoA.n7rcci9rf-3DzrUZOsnJnL7GioY +owner_id: '115997555619266561' +description: Bot created by Phantom, for a few servers of his own. +command_prefix: '!' +default_status: '!commands for a list of commands' +nsfw_channel: ['194979363911892992','183670904176246785'] + +db_host: 'localhost' +db_user_pass: 'rguFJ4tC22QKAMd6' +db_user: 'phxntx5_bonfire' + +db_default: 'phxntx5_bonfire' +db_boops: 'phxntx5_bonfire_boops' + +modCommands: + status: Changes the game the Bot is playing + say: Has the bot say whatever you tell it to, this will remove your message before the bot repeats it + volume: Sets the volume of the bot's 'voice' + pause: Pauses the current song + resume: Resumes the current paused song + stop: Stops playing audio and leaves the voice channel. This also clears the queue. + modskip: Forces a song skip, bypasses the skip vote system + +openCommands: + commands: Sends you a private message with a list of the commands you have access to + roll: Rolls a virtual six-sided die, and gives you the number of the roll + joke: Prints a random riddle, based on Unix's fortune riddle command + derpi: Provide a search term after the command to search derpibooru for the image, it will then provide a random image from the first page. Without any search terms, it will be completely random + battle: Mention someone in this message to setup a battle with them, the user can then accept or decline the battle, and each person has a 50/50 chance of winning + boop: Boops the person that is mentioned! Also saves and prints out the amount of times this user has booped the person mentioned + mostboops: Lets you know who you have booped the most, as well as the amount of times + mostwins: Prints a 'leaderboard' showing everyone's battling record, in order from the best to the worst + urban: Provides the top urbandictionary.com definition for the term(s) after the command + +voiceCommands: + join: Tells the bot to join a specific voice channel that you specify + summon: Pulls the bot into the voice channel you are in + play: Plays a song. If there is a song currently in the queue, then it is queued until the next song is done playing. This command automatically searches as well from YouTube. The list of supported sites can be found here https://rg3.github.io/youtube-dl/supportedsites.html + skip: Vote to skip a song. The song requester can automatically skip. 3 skip votes are needed for the song to be skipped. + playing: Shows information about the current song playing + +ownerCommands: + shutdown: Shuts down the bot completely + restart: Will restart the bot, useful for script changes + py: Evaluate any coding live, useful for live testing + avatar: Changes the avatar to what\'s provided, must be in the images/ folder + name: Changes the name of the bot + leave: Forces the bot to leave the server + +adminCommands: {} + +battleWins: ["A meteor fell on {1}, {0} is left standing and has been declared the victor!", + "{0} has bucked {1} into a tree, even Big Mac would be impressed at that kick!", + "As they were battling, {1} was struck by lightning! {0} you lucked out this time!", + "{1} tried to dive at {0} while fighting, somehow they missed and landed in quicksand. Try paying more attention next time {1}", + "{1} got a little...heated during the battle and ended up getting set on fire. {0} wins by remaining cool", + "Princess Celestia came in and banished {1} to the moon. Good luck getting into any battles up there", + "{1} took an arrow to the knee, they are no longer an adventurer. Keep on adventuring {0}", + "Common sense should make it obvious not to get into battle with {0}. Apparently {1} didn't get the memo", + "{0} had a nice cup of tea with {1} over their conflict, and mutually agreed that {0} was Best Pony", + "{0} and {1} had an intense staring contest. Sadly, {1} forgot to breathe and lost much more than the staring contest", + "It appears {1} is actually a pacifist, they ran away screaming and crying. Maybe you should have thought of that before getting in a fight?", + "A bunch of parasprites came in and ate up the jetpack while {1} was flying with it. Those pesky critters...", + "{0} used their charm to seduce {1} to surrender."] From 62ca5421b41a4a00421b8920bed1dad9975423ce Mon Sep 17 00:00:00 2001 From: Dan Hess Date: Thu, 7 Jul 2016 08:09:48 -0500 Subject: [PATCH 3/3] Create .gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7aa78f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Bot config file +config.yml