diff --git a/bot.py b/bot.py index 11b062d..fd014c4 100644 --- a/bot.py +++ b/bot.py @@ -1,610 +1,610 @@ -#!/usr/bin/env python3 -import discord -from discord.ext import commands, tasks -from discord.utils import get -from discord import FFmpegPCMAudio -from sqlite3 import connect -from datetime import datetime, timedelta -from config import * -from requests import get -from json import loads -from os.path import isfile -from os import rename, remove, listdir -from youtube_dl import YoutubeDL -from time import time -from asyncio import sleep -from threading import Thread -from io import BytesIO - -polls = {} -player = None #the Player for the music Bot -playerServer = None #The Server the player is active at the moment -audioPlayer = None -playerQue = [] -titleQue = [] -fileIndex = 0 -playing = False - -bot = commands.Bot(command_prefix=prefix) - -dbconnection = connect('database.db') -dbcursor = dbconnection.cursor() - -try: #try to create the database tables, if it fails they were created previosly - sql = "CREATE TABLE MESSAGES(user LONG, server LONG, textXP LONG, reactionXP LONG, vcXP LONG)" - sql2 = "CREATE TABLE VcActive (user LONG, startTime LONG)" - sql3 = "CREATE TABLE poll (pollID LONG, reaction STRING, calls LONG)" - dbcursor.execute(sql) - dbcursor.execute(sql2) - dbcursor.execute(sql3) -except: - pass - -@bot.event #print the username and id to the console and change the game status -async def on_ready(): - print('Logged in as') - print(bot.user.name) - print(bot.user.id) - print('------') - if(activity == 0): - await bot.change_presence(activity=discord.Game(name=name, start=datetime.fromtimestamp(gameStartTime))) - if(activity == 1): - await bot.change_presence(activity=discord.Streaming(name=name, url=url)) - if(activity == 2): - await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=name)) - if(activity == 3): - await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=name)) - -# -# -# _ __ ___ __ __ _ __ ___ ___ ___ ___ __ _ __ _ ___ -# | '_ \ / _ \\ \ /\ / / | '_ ` _ \ / _ \/ __|/ __| / _` | / _` | / _ \ -# | | | || __/ \ V V / | | | | | || __/\__ \\__ \| (_| || (_| || __/ -# |_| |_| \___| \_/\_/ |_| |_| |_| \___||___/|___/ \__,_| \__, | \___| -# __/ | -# |___/ - -@bot.event -async def on_message(message):#this will run on every message - if (message.guild == None) and "time" in message.content: #this is for fun, if the user sends the bot private a message with time in it, send him the current time - current_time = datetime.now().strftime("%H:%M:%S")#get the time as string - await message.author.send("Current time: " + current_time)#send the current time to the user as DM - return#don't execute the rest (will fail in the next step) - try: - if (len(allowOnServer) > 0) and message.guild.id not in allowOnServer:#check if whitelist is enabled and Server on list - return#don't execute the rest - except: - pass - - print(str(message.author) + " sent: " + str(message.content) + ", latency: " + str(bot.latency) + " ms, Bot: " + str(message.author.bot)) #print some information about the message to the console - if(message.author.bot) or (message.author.id == bot.user.id): #check if the bot has written the message, if yes stop parsing it - return - try: - res = dbcursor.execute("SELECT textXP FROM MESSAGES WHERE user=? AND server = ?", [message.author.id, message.guild.id]) #try to get the current textXP of the user - result = res.fetchone() - if (result == None): - dbcursor.execute("INSERT INTO MESSAGES VALUES (?, ?, ?, 0, 0)", [message.author.id, message.guild.id, messageXP]) #if Bot can't find the user in the Database create a new entry - else: - messages = result[0] + messageXP - dbcursor.execute("UPDATE MESSAGES SET textXP = ? WHERE user = ? AND server = ?", [messages, message.author.id, message.guild.id]) #update the textXP value in the Database - except: - pass - - dbconnection.commit() #write the database changes to disk - - if message.content.startswith(prefix): #check if the message starts with the prefix, if yes process the command - await bot.process_commands(message) - if "<@!" + str(bot.user.id) + ">" in message.content: - if (message.content[:3] == "hi ") or (message.content[-3:] == " hi"): - await message.channel.send("Hello <@!" + str(message.author.id) + "> To get a list of my commands enter " + prefix + "help.") - -# _ _ _ _ -# | | (_) | | | | -# _ __ ___ __ _ ___ | |_ _ ___ _ __ __ _ __| | __| | -# | '__| / _ \ / _` | / __|| __|| | / _ \ | '_ \ / _` | / _` | / _` | -# | | | __/| (_| || (__ | |_ | || (_) || | | | | (_| || (_| || (_| | -# |_| \___| \__,_| \___| \__||_| \___/ |_| |_| \__,_| \__,_| \__,_| -# -# - -@bot.event -async def on_raw_reaction_add(message): #this runs on every reaction - if(message.user_id == bot.user.id):#check if the bot has written the message, if yes stop parsing it - return - res = dbcursor.execute("SELECT reactionXP FROM MESSAGES WHERE user=? AND server = ?", [message.user_id, message.guild_id]) #try to get the reactionXP from the database - result = res.fetchone() - if (result == None): - dbcursor.execute("INSERT INTO MESSAGES VALUES (?, ?, 0, ?, 0)", [message.user_id, message.guild_id, reactionXP]) #if bot can't find the database entry for the user create a new one - messages = reactionXP - else: - messages = result[0] + reactionXP - dbcursor.execute("UPDATE MESSAGES SET reactionXP = ? WHERE user = ? AND server = ?", [messages, message.user_id, message.guild_id])#update the reactionXP with the new values - - try: - polls[str(message.message_id)][0] # check if the reaction is on a poll - calls = dbcursor.execute("SELECT calls FROM poll WHERE pollID = ? AND reaction = ?", [message.message_id, str(message.emoji)]).fetchone()[0]# if yes update the database and add 1 vote - dbcursor.execute("UPDATE poll SET calls = ? WHERE pollID = ? AND reaction = ?", [calls + 1, message.message_id, str(message.emoji)]) - except: - pass - - dbconnection.commit() #write the database changes to disk - -# _ _ -# | | (_) -# _ __ ___ __ _ ___ | |_ _ ___ _ __ _ __ ___ _ __ ___ ___ __ __ ___ -# | '__| / _ \ / _` | / __|| __|| | / _ \ | '_ \ | '__| / _ \| '_ ` _ \ / _ \ \ \ / / / _ \ -# | | | __/| (_| || (__ | |_ | || (_) || | | | | | | __/| | | | | || (_) | \ V / | __/ -# |_| \___| \__,_| \___| \__||_| \___/ |_| |_| |_| \___||_| |_| |_| \___/ \_/ \___| -# -# - -@bot.event -async def on_raw_reaction_remove(message): - try: - polls[str(message.message_id)][0] #check if the message is a poll - calls = dbcursor.execute("SELECT calls FROM poll WHERE pollID = ? AND reaction = ?", [message.message_id, str(message.emoji)]).fetchone()[0]#if yes update the database and remove 1 vote - dbcursor.execute("UPDATE poll SET calls = ? WHERE pollID = ? AND reaction = ?", [calls - 1, message.message_id, str(message.emoji)]) - except: - pass - dbconnection.commit()# write the database changes to disk - -# _ _ _ _ _ -# (_) | | | | | | | | -# __ __ ___ _ ___ ___ ___ | |__ __ _ | |_ _ _ _ __ __| | __ _ | |_ ___ -# \ \ / / / _ \ | | / __| / _ \ / __|| '_ \ / _` || __| | | | || '_ \ / _` | / _` || __| / _ \ -# \ V / | (_) || || (__ | __/| (__ | | | || (_| || |_ | |_| || |_) || (_| || (_| || |_ | __/ -# \_/ \___/ |_| \___| \___| \___||_| |_| \__,_| \__| \__,_|| .__/ \__,_| \__,_| \__| \___| -# | | -# |_| - -@bot.event -async def on_voice_state_update(member, before, after):#this runs on every voice chat update (user joins, leaves, go afk, moves, ...) - if member.bot or (member.id == bot.user.id): #check if the bot is the user, if yes stop parsing it - return - if not before.afk and after.afk:#check if the user moved from not afk to afk - print(str(member) + " is now AFK") - result = dbcursor.execute("SELECT startTime FROM VcActive WHERE user=?", [member.id]).fetchone()[0]#if yes, get the join time from the database - dbcursor.execute("DELETE FROM VcActive WHERE user=?", [member.id])#delete the entrie from the voicechat in the database - xp = (time() - result) / minutesPerVcXP#calculate the xp - result = dbcursor.execute("SELECT vcXP FROM MESSAGES WHERE user=? AND server = ?", [member.id, member.guild.id]).fetchone()[0]#update the user database and set the new Voice XP - dbcursor.execute("UPDATE MESSAGES SET vcXP = ? WHERE user = ? AND server = ?", [result + round(xp), member.id, member.guild.id]) - dbconnection.commit() #write the changes to the database - return - if before.afk and not after.afk: #check if the user moved from afk back to active - print(str(member) + " is active again") - dbcursor.execute("INSERT INTO VcActive VALUES (?, ?)", [member.id, round(time())])#insert the current time in the table - dbconnection.commit() #write the changes to database - return - if(after.channel == None): #check if the user left the voicechat - print(str(member) + " left the Voicechat") - result = dbcursor.execute("SELECT startTime FROM VcActive WHERE user = ?", [member.id]).fetchone()[0]#get the join time from database - dbcursor.execute("DELETE FROM VcActive WHERE user=?", [member.id])# delete the join time entry - xp = (time() - result) / 60 / minutesPerVcXP#calculate the XP - result = dbcursor.execute("SELECT vcXP FROM MESSAGES WHERE user=? AND server = ?", [member.id, member.guild.id]).fetchone()[0]#update the user Table and set the new voiceXP - dbcursor.execute("UPDATE MESSAGES SET vcXP = ? WHERE user = ? AND server = ?", [result + round(xp), member.id, member.guild.id]) - dbconnection.commit()#write the changes to disk - return - elif(before.channel == None): #check if a user joins the voicechat - print(str(member) + " joined the Voicechat " + str(after.channel)) - dbcursor.execute("INSERT INTO VcActive VALUES (?, ?)", [member.id, round(time())])#insert the current time in the database - dbconnection.commit()#write the changes to databasr - return -# _ _ -# | | | | -# | | ___ __ __ ___ | | -# | | / _ \\ \ / / / _ \| | -# | || __/ \ V / | __/| | -# |_| \___| \_/ \___||_| -# -# -@bot.command(brief="shows your current XP and level") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def level(ctx, user : discord.Member=None):#shows your current XP and level, as argument you can give a different user - if (user == None): #check if the message author submitted a different user to check, if not use the author - user = ctx.message.author - - xp = dbcursor.execute("SELECT textXP, reactionXP, vcXP FROM MESSAGES WHERE user=? AND server = ?", [user.id, ctx.message.guild.id]).fetchone()#get the xp of the user from database - unsorted = dbcursor.execute("SELECT user, textXP, reactionXP, vcXP FROM MESSAGES WHERE server = ?", [ctx.message.guild.id]).fetchall() #get all users from the database (for the ranking) - - ranking = [] #to this variable we will later add the users sorted - - while len(unsorted) > 0: #do this while we have entries in unsorted - lowest = unsorted[0]#just set the first entrie in lowest, so we can later overwrite it if we find a user with less XP - for entries in unsorted: #go through every user, calculate the XP and check if its lower than the current lowest - if((entries[1] + entries[2] + entries[3]) <= (lowest[1] + lowest[2] + lowest[3])): - lowest = entries#if the xp is lower, write the user to lowest - unsorted.remove(lowest)#after checking every user, remove the lowest from unsorted and add it to ranking - ranking.append(lowest) - - for entries in ranking:#try to find the user in the ranking array, to get his rank. - if(user.id == entries[0]): - rank = len(ranking) - ranking.index(entries) - - tempXP = xp[0]+xp[1]+xp[2]#the XP the user has in this level - level = 0#the current level - levelXP = 0#the XP the user needs for the level - while (tempXP > 0): #while the XP is over 0, calculate the XP needed fo this level and subtract it from tempXP - levelXP = base * (factor**(level)) - tempXP = tempXP - levelXP - level = level + 1 - - level = level - 1 #subtract one level, because the last level the user didn't reached - - username = str(user.name)#get the username - embed = discord.Embed(title="Stats of " + username , description="Rank " + str(rank) + " of " + str(len(ranking)))#make the response message - embed.set_thumbnail(url=user.avatar_url) - embed.add_field(name="Text XP", value=xp[0]) - embed.add_field(name="Reaction XP", value=xp[1]) - embed.add_field(name="Voice XP", value=xp[2]) - embed.add_field(name="Total XP", value=xp[0]+xp[1]+xp[2]) - embed.add_field(name="Level", value=level) - embed.add_field(name="Progress", value=str(round(levelXP+tempXP)) + "/" + str(round(levelXP)) + " (" + str(round((levelXP+tempXP) / levelXP * 100 )) + "%)") - - await ctx.message.channel.send(embed=embed) #send the response message - -# _ _ _ _ -# | | | | | | | | -# | | ___ __ _ __| | ___ _ __ | |__ ___ __ _ _ __ __| | -# | | / _ \ / _` | / _` | / _ \| '__|| '_ \ / _ \ / _` || '__| / _` | -# | || __/| (_| || (_| || __/| | | |_) || (_) || (_| || | | (_| | -# |_| \___| \__,_| \__,_| \___||_| |_.__/ \___/ \__,_||_| \__,_| -# -# - -@bot.command(brief="shows the leaderboard") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def leaderboard(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - unsorted = dbcursor.execute("SELECT user, textXP, reactionXP, vcXP FROM MESSAGES WHERE server = ?", [ctx.message.guild.id]).fetchall() #get all users from the database (for the ranking) - - ranking = [] #to this variable we will later add the users sorted - - while len(unsorted) > 0: #do this while we have entries in unsorted - lowest = unsorted[0]#just set the first entrie in lowest, so we can later overwrite it if we find a user with less XP - for entries in unsorted: #go through every user, calculate the XP and check if its lower than the current lowest - if((entries[1] + entries[2] + entries[3]) <= (lowest[1] + lowest[2] + lowest[3])): - lowest = entries#if the xp is lower, write the user to lowest - unsorted.remove(lowest)#after checking every user, remove the lowest from unsorted and add it to ranking - ranking.append(lowest) - - while len(ranking) > 10:#remove the last user, while the length is over 10 - ranking.remove(ranking[0]) - - badges = [":first_place: ", ":second_place: ", ":third_place: ", ":four: ", ":five: ", ":six: ", ":seven: ", ":eight: ", ":nine: ", ":keycap_ten: "]#the emoji in front of the username - message = ""#in this variable we will prepare in the next step the response - for entries in reversed(ranking):#loop through every user in reverse order to get the best first and andd his statistics to message - message = message + badges[len(ranking) - ranking.index(entries) - 1] + ": <@" + str(entries[0]) + "> **Total:** " + str(entries[1] + entries[2] + entries[3]) + " XP (**Text:** " + str(entries[1]) + " + **Reaction:** " + str(entries[2]) + " + **VC:** " + str(entries[3]) + ")\n" - embed = discord.Embed(title="Leaderboard" , description=message)#make the response Box - await ctx.message.channel.send(embed=embed)#print the ressponse box - -# _ -# (_) -# _ __ _ _ __ __ _ -# | '_ \ | || '_ \ / _` | -# | |_) || || | | || (_| | -# | .__/ |_||_| |_| \__, | -# | | __/ | -# |_| |___/ - -@bot.command(pass_context=True, brief="prints the ping time of the bot") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def ping(ctx):#prints the ping and the time the bot needed to process this command - now = datetime.utcnow()#get the current time - if reactOnValidCommand: await ctx.message.add_reaction("✅") - delta = round((now.microsecond - ctx.message.created_at.microsecond) /1000)#substract the time the message was created from the current time 8in microsecconds), convert this to millisecconds and round - embed = discord.Embed(title=":ping_pong: | Pong!", description="```prolog\nLatency:: " + str(round(bot.latency * 1000)) + "ms\nResponse :: " + str(delta) + "ms```")#make the response, we format it as code and select prolog as language for nice cloloring - - await ctx.message.channel.send(embed=embed)#send the prepared message - -# _ _ __ _ _ -# (_)(_) / _| | | | | -# ___ _ __ ___ ___ _ _ | |_ _ _ | |_ ___ __ __| |_ -# / _ \| '_ ` _ \ / _ \ | || || _|| | | | | __| / _ \\ \/ /| __| -# | __/| | | | | || (_) | | || || | | |_| | | |_ | __/ > < | |_ -# \___||_| |_| |_| \___/ | ||_||_| \__, | \__| \___|/_/\_\ \__| -# _/ | __/ | -# |__/ |___/ - -@bot.command(brief="prints the text as emojies") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def emoji(ctx, *message: str):#emojifies the string the user give as parameter - if reactOnValidCommand: await ctx.message.add_reaction("✅") - temp = "" - for messages in message:#because every word will be formatted as parameter by discord.py, we have to make a long string again - temp = temp + messages + " " - messageNew = "" - for char in temp:#process every character one by one - char = char.lower()#convert it to lower (e.g. Aa ==> aa) - if char in "abcdefghijklmnopqrstuvwxyz":#check if the char is a letter - messageNew = messageNew + ":regional_indicator_" + char + ":"#if yes we add to the new message the emoji of the letter - elif char in "1234567890":#if the character is a number we have to use a different emoji - numbers = { "0" : 'zero', "1" : 'one', "2" : 'two', "3" : 'three', "4" : 'four', "5": 'five', "6" : 'six', "7" : 'seven', "8" : 'eight', "9" : 'nine',} - messageNew = messageNew + ":" + numbers[char] + ":" - elif char == "?":#the emoji for ? - messageNew = messageNew + ":question:" - elif char == "!":#the emoji for ! - messageNew = messageNew + ":exclamation:" - else:#if we can'T find a suitable emoji, just add the raw char - messageNew = messageNew + char - await ctx.message.channel.send(messageNew)#send the formatted message - - -# _ _ -# | || | -# _ __ ___ | || | -# | '_ \ / _ \ | || | -# | |_) || (_) || || | -# | .__/ \___/ |_||_| -# | | -# |_| - -@bot.command(pass_context=True, brief="starts a poll", description="starts a poll, please give as first argument the question (for sentences please use \") and then the posibilities (leave empty for yes or no)") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def poll(ctx, question, *options: str):#this is a tool to create a poll - if reactOnValidCommand: await ctx.message.add_reaction("✅") - if len(options) <= 1:#check if options were supplied, if not add yes and no to the options - options = list(options) - options.append("yes") - options.append("no") - options = tuple(options) - if len(options) > 9:#we can only process 9 arguments at the moment (emoji 1 to 9) - await ctx.message.channel.send('You cannot make a poll for more than 9 things!')#if the user added more arguments, print a error message - return - - if len(options) == 2 and options[0] == 'yes' and options[1] == 'no':#select the reaction emojis, ✅ and ❌ for yes or no and numbers one to 9 for the rest - reactions = ['✅', '❌'] - else: - reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣'] - - description = "" - for x, option in enumerate(options):#make a dict from options and process every entry, x is the key and option the value - description += '\n {} {}'.format(reactions[x], option)#add to the description for every entry in otions one line with the emoji and the answer - embed = discord.Embed(title=question, description=description)#prepare the response - await ctx.message.channel.send("@everyone")#tag everyone, for the new survey - react_message = await ctx.message.channel.send(embed=embed)#print the survey and save the message to a variable - polls[str(react_message.id)] = [react_message, ctx.message.author.id]#save the react message and the userid to the polls array to acces it later - for reaction in reactions[:len(options)]:#for every reaction add an entry to the reaction database and add it to the message, that the user can click it - dbcursor.execute("INSERT INTO poll VALUES (?, ?, 0)", [react_message.id, reaction]) - await react_message.add_reaction(reaction) - - embed.set_footer(text='Poll ID: {}'.format(react_message.id))#add the poll id to the message (needed to get the results) - await react_message.edit(embed=embed) - -# _ _ _ _ -# | || | | || | -# _ __ ___ | || | _ __ ___ ___ _ _ | || |_ -# | '_ \ / _ \ | || | | '__| / _ \/ __|| | | || || __| -# | |_) || (_) || || | | | | __/\__ \| |_| || || |_ -# | .__/ \___/ |_||_| |_| \___||___/ \__,_||_| \__| -# | | -# |_| - -@bot.command(pass_context=True, brief="shows the result of a poll, give the poll ID as argument") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def result(ctx, id):#this function prints the result of a poll - if reactOnValidCommand: await ctx.message.add_reaction("✅") - poll_message = polls[str(id)][0]#get the poll message from the variable - poll_owner = polls[str(id)][1] - if(poll_owner != ctx.message.author.id): #check if the poll owner wants the result, or someone else - await ctx.message.channel.send("You can only get the results of your poll.") - return - - embed = poll_message.embeds[0]#get the embed (the content) - - del polls[str(id)]#delte the poll from the poll message array - unformatted_options = [x.strip() for x in poll_message.embeds[0].description.split('\n')]#split the poll options (split every line and remove all leading and trailing spaces) - opt_dict = {} - for options in unformatted_options:#for every option: split on the space between emoji and text and put it in a dict, with the emoji as key and the emoji + the text as value - options = options.split(" ", 1) - opt_dict[options[0]] = options[0] + " " + options[1] - - request = dbcursor.execute("SELECT calls, reaction FROM poll WHERE pollID = ?", [id]).fetchall() #get the votes from the database - description = "" - for results in request:#loop through the entries of the database and make the result - description = description + "\n" + opt_dict[results[1]] + ": " + str(results[0]) + " votes" - - - embed = discord.Embed(title=poll_message.embeds[0].title, description=description)#prepare the result - embed.set_footer(text='enden on ' + datetime.now().strftime("%d.%m.%Y %H:%M:%S"))#set as footer the end time of the poll - await poll_message.edit(embed=embed)#edit the origina lmessage and print the results - embed2 = discord.Embed(title=" ", description="You can find the result of the poll in the original message [here](" + poll_message.jump_url + ").")#This message will be printed to the end of the chat with a link to the original (where the results are) - await ctx.message.channel.send(embed=embed2)#print the result message from one line up - dbcursor.execute("DELETE FROM poll where pollID = ?", [id])#delete the entries from the poll in the database - dbconnection.commit()#write the database to disk - -# _____ -# |_ _| -# | | _ __ ___ __ _ __ _ ___ ___ -# | | | '_ ` _ \ / _` |/ _` |/ _ \/ __| -# _| |_| | | | | | (_| | (_| | __/\__ \ -# |_____|_| |_| |_|\__,_|\__, |\___||___/ -# __/ | -# |___/ - -@bot.command(pass_context=True, brief="sends a random cat image") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def cat(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - r = get("https://api.thecatapi.com/v1/images/search?size=full") # get random cat image - answer = loads(r.text) - extension = answer[0]["url"].split(".")[-1]#get the file extension - r = get(answer[0]["url"])#get the url to the cat - image_data = BytesIO(r.content)#download tthe image and load it into BytesIO - image_data.seek(0) - await ctx.send(file=discord.File(image_data, 'cat.' + extension))#send the cat image - -@bot.command(pass_context=True, brief="sends a random dog image") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def dog(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - r = get("https://random.dog/woof.json") # get random dog image - answer = loads(r.text) - extension = answer["url"].split(".")[-1]#get the file extension - r = get(answer["url"])#get the url to the dog - image_data = BytesIO(r.content)#download tthe image and load it into BytesIO - image_data.seek(0) - await ctx.send(file=discord.File(image_data, 'dog.' + extension))#send the dog image - -@bot.command(pass_context=True, brief="sends a random fail gif") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def fail(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - r = get("https://api.giphy.com/v1/gifs/random?api_key=0UTRbFtkMxAplrohufYco5IY74U8hOes&tag=fail&pg-13") # get random fail image - answer = loads(r.text) - r = get(answer["data"]["images"]["downsized_medium"]["url"])#get the url to the fail - image_data = BytesIO(r.content)#download tthe image and load it into BytesIO - image_data.seek(0) - await ctx.send(file=discord.File(image_data, 'fail.gif'))#send the fail image - -@bot.command(pass_context=True, brief="sends a random upload from jensmemes.tilera.xyz") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def jensmeme(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - r = get("https://jensmemes.tilera.xyz/api.php/random") # get random upload - await ctx.send(loads(r.text)["link"])#send the fail image -# ___ ___ _ ______ _ -# | \/ | (_) | ___ \ | | -# | . . | _ _ ___ _ ___ | |_/ / ___ | |_ -# | |\/| || | | |/ __|| | / __|| ___ \ / _ \ | __| -# | | | || |_| |\__ \| || (__ | |_/ /| (_) || |_ -# \_| |_/ \__,_||___/|_| \___|\____/ \___/ \__| -# -# - -@bot.command(brief="adds a song to the playlist") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def play(ctx, songURL : str): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - global player#import the global variables needed - global playerServer - global downloadQue - - try: - channel = ctx.author.voice.channel#get the current voicechat the user is in - except:#if we can't get the voice channel, the user is not in one ==> print an error - await ctx.message.channel.send("To use the music bot you have to be in a voice chat") - return - - if(player == None):#if the music player is currently not connected to a server - await channel.connect()#connect to this chat - player = ctx.voice_client #save the voice client for later use - playerServer = ctx.message.guild.id#save the music bot server id to variable - - if(playerServer != ctx.message.guild.id):#check if the music bot is at the moment on this server, if not print a error message - await ctx.message.channel.send("The Bot is currently connected to a different channel, please wait until he is finished there") - return - r = get("https://www.youtube.com/oembed?format=json&url=" + songURL)#check if video exists and get the title - if(r.status_code != 200):#if youtube doesn't respond with 200, there was an error finding the song - await ctx.message.channel.send("Can't find the song") - return - await ctx.message.channel.send("Adding `" + loads(r.text)["title"] + "` to que.")#print the user a status message - backgroundDownloader(loads(r.text)["html"].split("src=")[1][1:].split("\"")[0], loads(r.text)["title"]) - -@bot.command(brief="skipps the current song in playlist") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def skip(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - global player# import the global variable - player.stop()#stop the player (daemon think song enden ==> plays next one) - await ctx.message.channel.send("Skipped current song")#print the status to the console - -@bot.command(brief="Prints the current music que") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def que(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - global titleQue#import the global variable - message = "**Musicbot Que:**\n **[current] " + titleQue[0] + " **"#prepare the message and add the current playing first in bold - - for entries in titleQue[1:]: #scroll through the entries and add all songs to the resonse - message = message + "\n" + entries - - await ctx.message.channel.send(message)#send the prepared message - -@bot.command(brief="stop the music bot and clear the que") -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def stop(ctx): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - global player#import the global variables - global playerQue - global titleQue - global downloadQue - global playerServer - - if (playerServer != ctx.message.guild.id):#check if the music bot runns on this server - await ctx.message.channel.send("You can't stop the music bot on a different server.") - return - playerQue = []#delte all ques - titleQue = [] - downloadQue = [] - player.stop()#stop the current song - await player.disconnect()#disconnect from the voice chat - player = None#clear the player variable - playerServer = None#allow everyone to invite the music bot - for files in listdir("."):#delete all mp3 files - if files.endswith(".mp3"): - remove(files) - -@bot.command(brief="changes the volume of the music bot")#with this command you can change the volume of the music bot -@commands.cooldown(1, cooldownTime, commands.BucketType.user) -async def volume(ctx, volume : int): - if reactOnValidCommand: await ctx.message.add_reaction("✅") - global audioPlayer#import hte global audio player - if(volume < 0) or (volume > 100):#check if the volume is in a percent range - ctx.message.channel.send("Volume has to be between 0 and 100") - else: - audioPlayer.volume = volume / 100#change the volume - - - -async def checkForNextSong():#background task, that runns every seccond and checks if the song ended - global player#import the global variables - global playerQue - global playerServer - global playing - global titleQue - global audioPlayer - - while True: #catch all errors (dont crash when a error occurs) - try: - if(player != None) and not player.is_playing() and (len(playerQue) > 0):#check if the player is active and the song ended - if(playing):#check if the player played previously - remove(playerQue[0])#delete the last mp3 file - playerQue.remove(playerQue[0])#remove the first entry from the que and the first title - titleQue.remove(titleQue[0]) - playing = False#set playing to false - if(len(playerQue) == 0):#if the que is empty leave the voicechat and cleat the player variable - await player.disconnect() - player = None - playerServer = None#allow everyone to invite the music bot - for files in listdir("."):#delete all mp3 files - if files.endswith(".mp3"): - remove(files) - else: - playing = True#set playing to true and play the next file - print("playing " + playerQue[0]) - audioPlayer = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(playerQue[0]))#convert the audio player to a volume audio player - player.play(audioPlayer) - await sleep(1)#sleep 1 seccond and allo the bot to run different tasks - except: - pass - -def backgroundDownloader(url, title): - global playerQue#import the global variables - global fileIndex - ydl_opts = { #youtube-dl arguments - 'format': 'worstaudio/worst', - 'postprocessors': [{ - 'key': 'FFmpegExtractAudio', - 'preferredcodec': 'mp3', - 'preferredquality': '192', - }], - 'outtmpl': str(fileIndex) +'.%(ext)s', - } - fileIndex = fileIndex + 1 #add 1 to the file index counter (used to find the mp3 files easy later and that there are no files with the same name) - with YoutubeDL(ydl_opts) as ydl:#download the song with the arguments from up - ydl.download([url]) - playerQue.append(str(fileIndex - 1) + ".mp3")#add the file to the que - titleQue.append(title)#add the title to the que (for que list) - -bot.loop.create_task(checkForNextSong())#start the music bot task in background +#!/usr/bin/env python3 +import discord +from discord.ext import commands, tasks +from discord.utils import get +from discord import FFmpegPCMAudio +from sqlite3 import connect +from datetime import datetime, timedelta +from config import * +from requests import get +from json import loads +from os.path import isfile +from os import rename, remove, listdir +from youtube_dl import YoutubeDL +from time import time +from asyncio import sleep +from threading import Thread +from io import BytesIO + +polls = {} +player = None #the Player for the music Bot +playerServer = None #The Server the player is active at the moment +audioPlayer = None +playerQue = [] +titleQue = [] +fileIndex = 0 +playing = False + +bot = commands.Bot(command_prefix=prefix) + +dbconnection = connect('database.db') +dbcursor = dbconnection.cursor() + +try: #try to create the database tables, if it fails they were created previosly + sql = "CREATE TABLE MESSAGES(user LONG, server LONG, textXP LONG, reactionXP LONG, vcXP LONG)" + sql2 = "CREATE TABLE VcActive (user LONG, startTime LONG)" + sql3 = "CREATE TABLE poll (pollID LONG, reaction STRING, calls LONG)" + dbcursor.execute(sql) + dbcursor.execute(sql2) + dbcursor.execute(sql3) +except: + pass + +@bot.event #print the username and id to the console and change the game status +async def on_ready(): + print('Logged in as') + print(bot.user.name) + print(bot.user.id) + print('------') + if(activity == 0): + await bot.change_presence(activity=discord.Game(name=name, start=datetime.fromtimestamp(gameStartTime))) + if(activity == 1): + await bot.change_presence(activity=discord.Streaming(name=name, url=url)) + if(activity == 2): + await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=name)) + if(activity == 3): + await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=name)) + +# +# +# _ __ ___ __ __ _ __ ___ ___ ___ ___ __ _ __ _ ___ +# | '_ \ / _ \\ \ /\ / / | '_ ` _ \ / _ \/ __|/ __| / _` | / _` | / _ \ +# | | | || __/ \ V V / | | | | | || __/\__ \\__ \| (_| || (_| || __/ +# |_| |_| \___| \_/\_/ |_| |_| |_| \___||___/|___/ \__,_| \__, | \___| +# __/ | +# |___/ + +@bot.event +async def on_message(message):#this will run on every message + if (message.guild == None) and "time" in message.content: #this is for fun, if the user sends the bot private a message with time in it, send him the current time + current_time = datetime.now().strftime("%H:%M:%S")#get the time as string + await message.author.send("Current time: " + current_time)#send the current time to the user as DM + return#don't execute the rest (will fail in the next step) + try: + if (len(allowOnServer) > 0) and message.guild.id not in allowOnServer:#check if whitelist is enabled and Server on list + return#don't execute the rest + except: + pass + + print(str(message.author) + " sent: " + str(message.content) + ", latency: " + str(bot.latency) + " ms, Bot: " + str(message.author.bot)) #print some information about the message to the console + if(message.author.bot) or (message.author.id == bot.user.id): #check if the bot has written the message, if yes stop parsing it + return + try: + res = dbcursor.execute("SELECT textXP FROM MESSAGES WHERE user=? AND server = ?", [message.author.id, message.guild.id]) #try to get the current textXP of the user + result = res.fetchone() + if (result == None): + dbcursor.execute("INSERT INTO MESSAGES VALUES (?, ?, ?, 0, 0)", [message.author.id, message.guild.id, messageXP]) #if Bot can't find the user in the Database create a new entry + else: + messages = result[0] + messageXP + dbcursor.execute("UPDATE MESSAGES SET textXP = ? WHERE user = ? AND server = ?", [messages, message.author.id, message.guild.id]) #update the textXP value in the Database + except: + pass + + dbconnection.commit() #write the database changes to disk + + if message.content.startswith(prefix): #check if the message starts with the prefix, if yes process the command + await bot.process_commands(message) + if "<@!" + str(bot.user.id) + ">" in message.content: + if (message.content[:3] == "hi ") or (message.content[-3:] == " hi"): + await message.channel.send("Hello <@!" + str(message.author.id) + "> To get a list of my commands enter " + prefix + "help.") + +# _ _ _ _ +# | | (_) | | | | +# _ __ ___ __ _ ___ | |_ _ ___ _ __ __ _ __| | __| | +# | '__| / _ \ / _` | / __|| __|| | / _ \ | '_ \ / _` | / _` | / _` | +# | | | __/| (_| || (__ | |_ | || (_) || | | | | (_| || (_| || (_| | +# |_| \___| \__,_| \___| \__||_| \___/ |_| |_| \__,_| \__,_| \__,_| +# +# + +@bot.event +async def on_raw_reaction_add(message): #this runs on every reaction + if(message.user_id == bot.user.id):#check if the bot has written the message, if yes stop parsing it + return + res = dbcursor.execute("SELECT reactionXP FROM MESSAGES WHERE user=? AND server = ?", [message.user_id, message.guild_id]) #try to get the reactionXP from the database + result = res.fetchone() + if (result == None): + dbcursor.execute("INSERT INTO MESSAGES VALUES (?, ?, 0, ?, 0)", [message.user_id, message.guild_id, reactionXP]) #if bot can't find the database entry for the user create a new one + messages = reactionXP + else: + messages = result[0] + reactionXP + dbcursor.execute("UPDATE MESSAGES SET reactionXP = ? WHERE user = ? AND server = ?", [messages, message.user_id, message.guild_id])#update the reactionXP with the new values + + try: + polls[str(message.message_id)][0] # check if the reaction is on a poll + calls = dbcursor.execute("SELECT calls FROM poll WHERE pollID = ? AND reaction = ?", [message.message_id, str(message.emoji)]).fetchone()[0]# if yes update the database and add 1 vote + dbcursor.execute("UPDATE poll SET calls = ? WHERE pollID = ? AND reaction = ?", [calls + 1, message.message_id, str(message.emoji)]) + except: + pass + + dbconnection.commit() #write the database changes to disk + +# _ _ +# | | (_) +# _ __ ___ __ _ ___ | |_ _ ___ _ __ _ __ ___ _ __ ___ ___ __ __ ___ +# | '__| / _ \ / _` | / __|| __|| | / _ \ | '_ \ | '__| / _ \| '_ ` _ \ / _ \ \ \ / / / _ \ +# | | | __/| (_| || (__ | |_ | || (_) || | | | | | | __/| | | | | || (_) | \ V / | __/ +# |_| \___| \__,_| \___| \__||_| \___/ |_| |_| |_| \___||_| |_| |_| \___/ \_/ \___| +# +# + +@bot.event +async def on_raw_reaction_remove(message): + try: + polls[str(message.message_id)][0] #check if the message is a poll + calls = dbcursor.execute("SELECT calls FROM poll WHERE pollID = ? AND reaction = ?", [message.message_id, str(message.emoji)]).fetchone()[0]#if yes update the database and remove 1 vote + dbcursor.execute("UPDATE poll SET calls = ? WHERE pollID = ? AND reaction = ?", [calls - 1, message.message_id, str(message.emoji)]) + except: + pass + dbconnection.commit()# write the database changes to disk + +# _ _ _ _ _ +# (_) | | | | | | | | +# __ __ ___ _ ___ ___ ___ | |__ __ _ | |_ _ _ _ __ __| | __ _ | |_ ___ +# \ \ / / / _ \ | | / __| / _ \ / __|| '_ \ / _` || __| | | | || '_ \ / _` | / _` || __| / _ \ +# \ V / | (_) || || (__ | __/| (__ | | | || (_| || |_ | |_| || |_) || (_| || (_| || |_ | __/ +# \_/ \___/ |_| \___| \___| \___||_| |_| \__,_| \__| \__,_|| .__/ \__,_| \__,_| \__| \___| +# | | +# |_| + +@bot.event +async def on_voice_state_update(member, before, after):#this runs on every voice chat update (user joins, leaves, go afk, moves, ...) + if member.bot or (member.id == bot.user.id): #check if the bot is the user, if yes stop parsing it + return + if not before.afk and after.afk:#check if the user moved from not afk to afk + print(str(member) + " is now AFK") + result = dbcursor.execute("SELECT startTime FROM VcActive WHERE user=?", [member.id]).fetchone()[0]#if yes, get the join time from the database + dbcursor.execute("DELETE FROM VcActive WHERE user=?", [member.id])#delete the entrie from the voicechat in the database + xp = (time() - result) / minutesPerVcXP#calculate the xp + result = dbcursor.execute("SELECT vcXP FROM MESSAGES WHERE user=? AND server = ?", [member.id, member.guild.id]).fetchone()[0]#update the user database and set the new Voice XP + dbcursor.execute("UPDATE MESSAGES SET vcXP = ? WHERE user = ? AND server = ?", [result + round(xp), member.id, member.guild.id]) + dbconnection.commit() #write the changes to the database + return + if before.afk and not after.afk: #check if the user moved from afk back to active + print(str(member) + " is active again") + dbcursor.execute("INSERT INTO VcActive VALUES (?, ?)", [member.id, round(time())])#insert the current time in the table + dbconnection.commit() #write the changes to database + return + if(after.channel == None): #check if the user left the voicechat + print(str(member) + " left the Voicechat") + result = dbcursor.execute("SELECT startTime FROM VcActive WHERE user = ?", [member.id]).fetchone()[0]#get the join time from database + dbcursor.execute("DELETE FROM VcActive WHERE user=?", [member.id])# delete the join time entry + xp = (time() - result) / 60 / minutesPerVcXP#calculate the XP + result = dbcursor.execute("SELECT vcXP FROM MESSAGES WHERE user=? AND server = ?", [member.id, member.guild.id]).fetchone()[0]#update the user Table and set the new voiceXP + dbcursor.execute("UPDATE MESSAGES SET vcXP = ? WHERE user = ? AND server = ?", [result + round(xp), member.id, member.guild.id]) + dbconnection.commit()#write the changes to disk + return + elif(before.channel == None): #check if a user joins the voicechat + print(str(member) + " joined the Voicechat " + str(after.channel)) + dbcursor.execute("INSERT INTO VcActive VALUES (?, ?)", [member.id, round(time())])#insert the current time in the database + dbconnection.commit()#write the changes to databasr + return +# _ _ +# | | | | +# | | ___ __ __ ___ | | +# | | / _ \\ \ / / / _ \| | +# | || __/ \ V / | __/| | +# |_| \___| \_/ \___||_| +# +# +@bot.command(brief="shows your current XP and level") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def level(ctx, user : discord.Member=None):#shows your current XP and level, as argument you can give a different user + if (user == None): #check if the message author submitted a different user to check, if not use the author + user = ctx.message.author + + xp = dbcursor.execute("SELECT textXP, reactionXP, vcXP FROM MESSAGES WHERE user=? AND server = ?", [user.id, ctx.message.guild.id]).fetchone()#get the xp of the user from database + unsorted = dbcursor.execute("SELECT user, textXP, reactionXP, vcXP FROM MESSAGES WHERE server = ?", [ctx.message.guild.id]).fetchall() #get all users from the database (for the ranking) + + ranking = [] #to this variable we will later add the users sorted + + while len(unsorted) > 0: #do this while we have entries in unsorted + lowest = unsorted[0]#just set the first entrie in lowest, so we can later overwrite it if we find a user with less XP + for entries in unsorted: #go through every user, calculate the XP and check if its lower than the current lowest + if((entries[1] + entries[2] + entries[3]) <= (lowest[1] + lowest[2] + lowest[3])): + lowest = entries#if the xp is lower, write the user to lowest + unsorted.remove(lowest)#after checking every user, remove the lowest from unsorted and add it to ranking + ranking.append(lowest) + + for entries in ranking:#try to find the user in the ranking array, to get his rank. + if(user.id == entries[0]): + rank = len(ranking) - ranking.index(entries) + + tempXP = xp[0]+xp[1]+xp[2]#the XP the user has in this level + level = 0#the current level + levelXP = 0#the XP the user needs for the level + while (tempXP > 0): #while the XP is over 0, calculate the XP needed fo this level and subtract it from tempXP + levelXP = base * (factor**(level)) + tempXP = tempXP - levelXP + level = level + 1 + + level = level - 1 #subtract one level, because the last level the user didn't reached + + username = str(user.name)#get the username + embed = discord.Embed(title="Stats of " + username , description="Rank " + str(rank) + " of " + str(len(ranking)))#make the response message + embed.set_thumbnail(url=user.avatar_url) + embed.add_field(name="Text XP", value=xp[0]) + embed.add_field(name="Reaction XP", value=xp[1]) + embed.add_field(name="Voice XP", value=xp[2]) + embed.add_field(name="Total XP", value=xp[0]+xp[1]+xp[2]) + embed.add_field(name="Level", value=level) + embed.add_field(name="Progress", value=str(round(levelXP+tempXP)) + "/" + str(round(levelXP)) + " (" + str(round((levelXP+tempXP) / levelXP * 100 )) + "%)") + + await ctx.message.channel.send(embed=embed) #send the response message + +# _ _ _ _ +# | | | | | | | | +# | | ___ __ _ __| | ___ _ __ | |__ ___ __ _ _ __ __| | +# | | / _ \ / _` | / _` | / _ \| '__|| '_ \ / _ \ / _` || '__| / _` | +# | || __/| (_| || (_| || __/| | | |_) || (_) || (_| || | | (_| | +# |_| \___| \__,_| \__,_| \___||_| |_.__/ \___/ \__,_||_| \__,_| +# +# + +@bot.command(brief="shows the leaderboard") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def leaderboard(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + unsorted = dbcursor.execute("SELECT user, textXP, reactionXP, vcXP FROM MESSAGES WHERE server = ?", [ctx.message.guild.id]).fetchall() #get all users from the database (for the ranking) + + ranking = [] #to this variable we will later add the users sorted + + while len(unsorted) > 0: #do this while we have entries in unsorted + lowest = unsorted[0]#just set the first entrie in lowest, so we can later overwrite it if we find a user with less XP + for entries in unsorted: #go through every user, calculate the XP and check if its lower than the current lowest + if((entries[1] + entries[2] + entries[3]) <= (lowest[1] + lowest[2] + lowest[3])): + lowest = entries#if the xp is lower, write the user to lowest + unsorted.remove(lowest)#after checking every user, remove the lowest from unsorted and add it to ranking + ranking.append(lowest) + + while len(ranking) > 10:#remove the last user, while the length is over 10 + ranking.remove(ranking[0]) + + badges = [":first_place: ", ":second_place: ", ":third_place: ", ":four: ", ":five: ", ":six: ", ":seven: ", ":eight: ", ":nine: ", ":keycap_ten: "]#the emoji in front of the username + message = ""#in this variable we will prepare in the next step the response + for entries in reversed(ranking):#loop through every user in reverse order to get the best first and andd his statistics to message + message = message + badges[len(ranking) - ranking.index(entries) - 1] + ": <@" + str(entries[0]) + "> **Total:** " + str(entries[1] + entries[2] + entries[3]) + " XP (**Text:** " + str(entries[1]) + " + **Reaction:** " + str(entries[2]) + " + **VC:** " + str(entries[3]) + ")\n" + embed = discord.Embed(title="Leaderboard" , description=message)#make the response Box + await ctx.message.channel.send(embed=embed)#print the ressponse box + +# _ +# (_) +# _ __ _ _ __ __ _ +# | '_ \ | || '_ \ / _` | +# | |_) || || | | || (_| | +# | .__/ |_||_| |_| \__, | +# | | __/ | +# |_| |___/ + +@bot.command(pass_context=True, brief="prints the ping time of the bot") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def ping(ctx):#prints the ping and the time the bot needed to process this command + now = datetime.utcnow()#get the current time + if reactOnValidCommand: await ctx.message.add_reaction("✅") + delta = round((now.microsecond - ctx.message.created_at.microsecond) /1000)#substract the time the message was created from the current time 8in microsecconds), convert this to millisecconds and round + embed = discord.Embed(title=":ping_pong: | Pong!", description="```prolog\nLatency:: " + str(round(bot.latency * 1000)) + "ms\nResponse :: " + str(delta) + "ms```")#make the response, we format it as code and select prolog as language for nice cloloring + + await ctx.message.channel.send(embed=embed)#send the prepared message + +# _ _ __ _ _ +# (_)(_) / _| | | | | +# ___ _ __ ___ ___ _ _ | |_ _ _ | |_ ___ __ __| |_ +# / _ \| '_ ` _ \ / _ \ | || || _|| | | | | __| / _ \\ \/ /| __| +# | __/| | | | | || (_) | | || || | | |_| | | |_ | __/ > < | |_ +# \___||_| |_| |_| \___/ | ||_||_| \__, | \__| \___|/_/\_\ \__| +# _/ | __/ | +# |__/ |___/ + +@bot.command(brief="prints the text as emojies") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def emoji(ctx, *message: str):#emojifies the string the user give as parameter + if reactOnValidCommand: await ctx.message.add_reaction("✅") + temp = "" + for messages in message:#because every word will be formatted as parameter by discord.py, we have to make a long string again + temp = temp + messages + " " + messageNew = "" + for char in temp:#process every character one by one + char = char.lower()#convert it to lower (e.g. Aa ==> aa) + if char in "abcdefghijklmnopqrstuvwxyz":#check if the char is a letter + messageNew = messageNew + ":regional_indicator_" + char + ":"#if yes we add to the new message the emoji of the letter + elif char in "1234567890":#if the character is a number we have to use a different emoji + numbers = { "0" : 'zero', "1" : 'one', "2" : 'two', "3" : 'three', "4" : 'four', "5": 'five', "6" : 'six', "7" : 'seven', "8" : 'eight', "9" : 'nine',} + messageNew = messageNew + ":" + numbers[char] + ":" + elif char == "?":#the emoji for ? + messageNew = messageNew + ":question:" + elif char == "!":#the emoji for ! + messageNew = messageNew + ":exclamation:" + else:#if we can'T find a suitable emoji, just add the raw char + messageNew = messageNew + char + await ctx.message.channel.send(messageNew)#send the formatted message + + +# _ _ +# | || | +# _ __ ___ | || | +# | '_ \ / _ \ | || | +# | |_) || (_) || || | +# | .__/ \___/ |_||_| +# | | +# |_| + +@bot.command(pass_context=True, brief="starts a poll", description="starts a poll, please give as first argument the question (for sentences please use \") and then the posibilities (leave empty for yes or no)") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def poll(ctx, question, *options: str):#this is a tool to create a poll + if reactOnValidCommand: await ctx.message.add_reaction("✅") + if len(options) <= 1:#check if options were supplied, if not add yes and no to the options + options = list(options) + options.append("yes") + options.append("no") + options = tuple(options) + if len(options) > 9:#we can only process 9 arguments at the moment (emoji 1 to 9) + await ctx.message.channel.send('You cannot make a poll for more than 9 things!')#if the user added more arguments, print a error message + return + + if len(options) == 2 and options[0] == 'yes' and options[1] == 'no':#select the reaction emojis, ✅ and ❌ for yes or no and numbers one to 9 for the rest + reactions = ['✅', '❌'] + else: + reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣'] + + description = "" + for x, option in enumerate(options):#make a dict from options and process every entry, x is the key and option the value + description += '\n {} {}'.format(reactions[x], option)#add to the description for every entry in otions one line with the emoji and the answer + embed = discord.Embed(title=question, description=description)#prepare the response + await ctx.message.channel.send("@everyone")#tag everyone, for the new survey + react_message = await ctx.message.channel.send(embed=embed)#print the survey and save the message to a variable + polls[str(react_message.id)] = [react_message, ctx.message.author.id]#save the react message and the userid to the polls array to acces it later + for reaction in reactions[:len(options)]:#for every reaction add an entry to the reaction database and add it to the message, that the user can click it + dbcursor.execute("INSERT INTO poll VALUES (?, ?, 0)", [react_message.id, reaction]) + await react_message.add_reaction(reaction) + + embed.set_footer(text='Poll ID: {}'.format(react_message.id))#add the poll id to the message (needed to get the results) + await react_message.edit(embed=embed) + +# _ _ _ _ +# | || | | || | +# _ __ ___ | || | _ __ ___ ___ _ _ | || |_ +# | '_ \ / _ \ | || | | '__| / _ \/ __|| | | || || __| +# | |_) || (_) || || | | | | __/\__ \| |_| || || |_ +# | .__/ \___/ |_||_| |_| \___||___/ \__,_||_| \__| +# | | +# |_| + +@bot.command(pass_context=True, brief="shows the result of a poll, give the poll ID as argument") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def result(ctx, id):#this function prints the result of a poll + if reactOnValidCommand: await ctx.message.add_reaction("✅") + poll_message = polls[str(id)][0]#get the poll message from the variable + poll_owner = polls[str(id)][1] + if(poll_owner != ctx.message.author.id): #check if the poll owner wants the result, or someone else + await ctx.message.channel.send("You can only get the results of your poll.") + return + + embed = poll_message.embeds[0]#get the embed (the content) + + del polls[str(id)]#delte the poll from the poll message array + unformatted_options = [x.strip() for x in poll_message.embeds[0].description.split('\n')]#split the poll options (split every line and remove all leading and trailing spaces) + opt_dict = {} + for options in unformatted_options:#for every option: split on the space between emoji and text and put it in a dict, with the emoji as key and the emoji + the text as value + options = options.split(" ", 1) + opt_dict[options[0]] = options[0] + " " + options[1] + + request = dbcursor.execute("SELECT calls, reaction FROM poll WHERE pollID = ?", [id]).fetchall() #get the votes from the database + description = "" + for results in request:#loop through the entries of the database and make the result + description = description + "\n" + opt_dict[results[1]] + ": " + str(results[0]) + " votes" + + + embed = discord.Embed(title=poll_message.embeds[0].title, description=description)#prepare the result + embed.set_footer(text='enden on ' + datetime.now().strftime("%d.%m.%Y %H:%M:%S"))#set as footer the end time of the poll + await poll_message.edit(embed=embed)#edit the origina lmessage and print the results + embed2 = discord.Embed(title=" ", description="You can find the result of the poll in the original message [here](" + poll_message.jump_url + ").")#This message will be printed to the end of the chat with a link to the original (where the results are) + await ctx.message.channel.send(embed=embed2)#print the result message from one line up + dbcursor.execute("DELETE FROM poll where pollID = ?", [id])#delete the entries from the poll in the database + dbconnection.commit()#write the database to disk + +# _____ +# |_ _| +# | | _ __ ___ __ _ __ _ ___ ___ +# | | | '_ ` _ \ / _` |/ _` |/ _ \/ __| +# _| |_| | | | | | (_| | (_| | __/\__ \ +# |_____|_| |_| |_|\__,_|\__, |\___||___/ +# __/ | +# |___/ + +@bot.command(pass_context=True, brief="sends a random cat image") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def cat(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + r = get("https://api.thecatapi.com/v1/images/search?size=full") # get random cat image + answer = loads(r.text) + extension = answer[0]["url"].split(".")[-1]#get the file extension + r = get(answer[0]["url"])#get the url to the cat + image_data = BytesIO(r.content)#download tthe image and load it into BytesIO + image_data.seek(0) + await ctx.send(file=discord.File(image_data, 'cat.' + extension))#send the cat image + +@bot.command(pass_context=True, brief="sends a random dog image") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def dog(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + r = get("https://random.dog/woof.json") # get random dog image + answer = loads(r.text) + extension = answer["url"].split(".")[-1]#get the file extension + r = get(answer["url"])#get the url to the dog + image_data = BytesIO(r.content)#download tthe image and load it into BytesIO + image_data.seek(0) + await ctx.send(file=discord.File(image_data, 'dog.' + extension))#send the dog image + +@bot.command(pass_context=True, brief="sends a random fail gif") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def fail(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + r = get("https://api.giphy.com/v1/gifs/random?api_key=0UTRbFtkMxAplrohufYco5IY74U8hOes&tag=fail&pg-13") # get random fail image + answer = loads(r.text) + r = get(answer["data"]["images"]["downsized_medium"]["url"])#get the url to the fail + image_data = BytesIO(r.content)#download tthe image and load it into BytesIO + image_data.seek(0) + await ctx.send(file=discord.File(image_data, 'fail.gif'))#send the fail image + +@bot.command(pass_context=True, brief="sends a random upload from jensmemes.tilera.xyz") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def jensmeme(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + r = get("https://jensmemes.tilera.xyz/api/random") # get random upload + await ctx.send(loads(r.text)["link"])#send the fail image +# ___ ___ _ ______ _ +# | \/ | (_) | ___ \ | | +# | . . | _ _ ___ _ ___ | |_/ / ___ | |_ +# | |\/| || | | |/ __|| | / __|| ___ \ / _ \ | __| +# | | | || |_| |\__ \| || (__ | |_/ /| (_) || |_ +# \_| |_/ \__,_||___/|_| \___|\____/ \___/ \__| +# +# + +@bot.command(brief="adds a song to the playlist") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def play(ctx, songURL : str): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + global player#import the global variables needed + global playerServer + global downloadQue + + try: + channel = ctx.author.voice.channel#get the current voicechat the user is in + except:#if we can't get the voice channel, the user is not in one ==> print an error + await ctx.message.channel.send("To use the music bot you have to be in a voice chat") + return + + if(player == None):#if the music player is currently not connected to a server + await channel.connect()#connect to this chat + player = ctx.voice_client #save the voice client for later use + playerServer = ctx.message.guild.id#save the music bot server id to variable + + if(playerServer != ctx.message.guild.id):#check if the music bot is at the moment on this server, if not print a error message + await ctx.message.channel.send("The Bot is currently connected to a different channel, please wait until he is finished there") + return + r = get("https://www.youtube.com/oembed?format=json&url=" + songURL)#check if video exists and get the title + if(r.status_code != 200):#if youtube doesn't respond with 200, there was an error finding the song + await ctx.message.channel.send("Can't find the song") + return + await ctx.message.channel.send("Adding `" + loads(r.text)["title"] + "` to que.")#print the user a status message + backgroundDownloader(loads(r.text)["html"].split("src=")[1][1:].split("\"")[0], loads(r.text)["title"]) + +@bot.command(brief="skipps the current song in playlist") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def skip(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + global player# import the global variable + player.stop()#stop the player (daemon think song enden ==> plays next one) + await ctx.message.channel.send("Skipped current song")#print the status to the console + +@bot.command(brief="Prints the current music que") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def que(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + global titleQue#import the global variable + message = "**Musicbot Que:**\n **[current] " + titleQue[0] + " **"#prepare the message and add the current playing first in bold + + for entries in titleQue[1:]: #scroll through the entries and add all songs to the resonse + message = message + "\n" + entries + + await ctx.message.channel.send(message)#send the prepared message + +@bot.command(brief="stop the music bot and clear the que") +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def stop(ctx): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + global player#import the global variables + global playerQue + global titleQue + global downloadQue + global playerServer + + if (playerServer != ctx.message.guild.id):#check if the music bot runns on this server + await ctx.message.channel.send("You can't stop the music bot on a different server.") + return + playerQue = []#delte all ques + titleQue = [] + downloadQue = [] + player.stop()#stop the current song + await player.disconnect()#disconnect from the voice chat + player = None#clear the player variable + playerServer = None#allow everyone to invite the music bot + for files in listdir("."):#delete all mp3 files + if files.endswith(".mp3"): + remove(files) + +@bot.command(brief="changes the volume of the music bot")#with this command you can change the volume of the music bot +@commands.cooldown(1, cooldownTime, commands.BucketType.user) +async def volume(ctx, volume : int): + if reactOnValidCommand: await ctx.message.add_reaction("✅") + global audioPlayer#import hte global audio player + if(volume < 0) or (volume > 100):#check if the volume is in a percent range + ctx.message.channel.send("Volume has to be between 0 and 100") + else: + audioPlayer.volume = volume / 100#change the volume + + + +async def checkForNextSong():#background task, that runns every seccond and checks if the song ended + global player#import the global variables + global playerQue + global playerServer + global playing + global titleQue + global audioPlayer + + while True: #catch all errors (dont crash when a error occurs) + try: + if(player != None) and not player.is_playing() and (len(playerQue) > 0):#check if the player is active and the song ended + if(playing):#check if the player played previously + remove(playerQue[0])#delete the last mp3 file + playerQue.remove(playerQue[0])#remove the first entry from the que and the first title + titleQue.remove(titleQue[0]) + playing = False#set playing to false + if(len(playerQue) == 0):#if the que is empty leave the voicechat and cleat the player variable + await player.disconnect() + player = None + playerServer = None#allow everyone to invite the music bot + for files in listdir("."):#delete all mp3 files + if files.endswith(".mp3"): + remove(files) + else: + playing = True#set playing to true and play the next file + print("playing " + playerQue[0]) + audioPlayer = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(playerQue[0]))#convert the audio player to a volume audio player + player.play(audioPlayer) + await sleep(1)#sleep 1 seccond and allo the bot to run different tasks + except: + pass + +def backgroundDownloader(url, title): + global playerQue#import the global variables + global fileIndex + ydl_opts = { #youtube-dl arguments + 'format': 'worstaudio/worst', + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }], + 'outtmpl': str(fileIndex) +'.%(ext)s', + } + fileIndex = fileIndex + 1 #add 1 to the file index counter (used to find the mp3 files easy later and that there are no files with the same name) + with YoutubeDL(ydl_opts) as ydl:#download the song with the arguments from up + ydl.download([url]) + playerQue.append(str(fileIndex - 1) + ".mp3")#add the file to the que + titleQue.append(title)#add the title to the que (for que list) + +bot.loop.create_task(checkForNextSong())#start the music bot task in background bot.run(token, bot=botAccount)#start the bot with the options in config.py \ No newline at end of file