async def rmvcmd(self, ctx, name): with open('../config/custom commands.json', 'r', encoding='UTF-8') as f: cmds = json.load(f) if name.startswith('.') == False: name = '.' + name if name in cmds: if 'protected' in cmds[name] and cmds[name]['protected'] == True: await ctx.send( f'Sorry {ctx.author.mention} but this is a protected command. You need to contact the developer to remove this one' ) ConsoleMessage( f'{ctx.author} attempted to remove command {name}') return else: del cmds[name] ConsoleMessage( f'{ctx.author} removed the custom command: {name}') await ctx.send( f'{name} has been successfully removed from the custom commands list!' ) return else: await ctx.send( f'I\'m sorry {ctx.author.mention} but the custom command {name} doesn\'t appear to exist' )
async def clear(self, ctx, amount=1): await ctx.channel.purge(limit=amount + 1) #informs the console of the changes and adds to clear counter in "usage.json" if amount > 1: ConsoleMessage( f'{ctx.author} cleared {amount} messages from channel #{ctx.channel.name}' ) add_usage("messages cleared", amount) elif amount == 1: ConsoleMessage( f'{ctx.author} cleared 1 message from channel #{ctx.channel.name}' ) add_usage("messages cleared")
async def removerole(self, ctx, role): if role.startswith('<@&') and role.endswith('>') and len(role) == 22: role = role[3:] tmp = get(ctx.guild.roles, id=int(role[:-1])) if tmp != None: role = tmp.name found = False for react, rl in self.roleData.items(): if role == rl: del self.roleData[react] found = True break if found: try: with open(f'{PATH}\\config\\roles.json', 'r') as f: data = json.load(f) rolenames = list(self.roleData.values()) reactnames = list(self.roleData.keys()) data['roles'] = [] for i in range(len(rolenames)): data['roles'].append({ 'role name': rolenames[i], 'reaction': reactnames[i] }) with open(f'{PATH}\\config\\roles.json', 'w') as f: json.dump(data, f, indent=4) rl = get(ctx.guild.roles, name=role) if rl != None: out = f'Successfully removed {rl.mention} from the self assigning list' else: out = f'Successfully removed {role} from the self assigning list' ConsoleMessage( f'{ctx.author} removed "{role}" as an assignable role') except Exception as e: ErrorLog(e) else: rl = get(ctx.guild.roles, name=role) if rl != None: out = f'I\'m sorry {ctx.author.mention} but the role {rl} doesn\'t appear to be assigned to anything' ConsoleMessage( f'{ctx.author} attempted to remove {role}" as an assignable role but it does not appear to be on the list' ) else: out = f'I\'m sorry {ctx.author.mention} but the role {role} doesn\'t appear to be assigned to anything' ConsoleMessage( f'{ctx.author} attempted to remove {role}" as an assignable role but it does not appear to exist' ) await ctx.send(out)
async def meabhISFJ(self): while True: await asyncio.sleep(600) with open(f'{PATH}\\data\\tmpdata\\timestamps.json', 'r') as f: tmp = json.load(f) prev = tmp['16 personality'] tmp = tmp['Meabhs 16'] if tmp != datetime.today().strftime("%Y-%m-%d"): ConsoleMessage('Posted 16 personality MOTD to Meabh') self.meabh = datetime.today().strftime("%Y-%m-%d") self.timestamp = { '16 personality': prev, 'Meabhs 16': datetime.today().strftime("%Y-%m-%d") } with open(f'{PATH}\\data\\tmpdata\\timestamps.json', 'w') as f: json.dump(self.timestamp, f, indent=4) if self.meabhuser == None: self.meabhuser = await self.client.fetch_user( 185869510706855937) url = 'https://www.16personalities.com/isfj-personality' img = Image.open(f'{PATH}\\resources\\images\\isfj.png') draw = ImageDraw.Draw(img) src = requests.get(url).text soup = bs(src, 'html.parser') motd = soup.find('div', attrs={'class': 'insight'}) motd = str(motd)[21:-6] motd_lines = [] size = self.arial.getsize(motd)[0] motd_lines = (self.fitbox(motd)) j = 0 for line in motd_lines: draw.text((205 - (self.arial.getsize(line)[0] / 2), 165 + ((self.arial.getsize('YE')[1] + 3) * j)), line, font=self.arial, fill=(255, 255, 255)) j += 1 img.save(f'{PATH}\\resources\\images\\tmp\\meabhmsg.png') try: await self.meabhuser.send(file=discord.File( f'{PATH}\\resources\\images\\tmp\\meabhmsg.png')) except: ConsoleMessage('MOTD failed to send to Meabh') #await self.channel.send(f'Ignore this message, this is for testing purposes\n{urluser}') os.remove(f'{PATH}\\resources\\images\\tmp\\meabhmsg.png')
async def vote_error(self, ctx, error): if isinstance(error, commands.CheckFailure): ConsoleMessage(f'{ctx.author} attempted to use .vote in #{ctx.channel}') await ctx.message.delete() else: ErrorLog(error) await ctx.message.delete()
async def changestatus(self, ctx, statType, *, status): #creates the action that the bot is doing based on input if statType.lower() == 'watching': customAct = discord.Activity(type=discord.ActivityType.watching, name=status) elif statType.lower() == 'listening': customAct = discord.Activity(type=discord.ActivityType.listening, name=status) elif statType.lower() == 'playing': customAct = discord.Game(status) #if the inputted action is not on the valid list, inform user and give valid examples else: await ctx.send( f'Sorry {ctx.author.mention}, but I need to be playing, watching or listening to something\nE.g: `.changestatus playing Mario` means that I can play Mario! :smile:' ) return #changes the status of the bot await self.client.change_presence(status=discord.Status.idle, activity=customAct) #informs the console of the change and adds data to "usage.json" ConsoleMessage( f'{ctx.author} changed the status to {statType} {status}') add_usage("status changes")
async def endpoll(self,ctx): voted = False try: with open(f'{PATH}\\data\\polls\\poll{ctx.channel.id}.json','r') as f: stats = json.load(f) except: await ctx.send(f'There doesn\'t seem to be any active polls in this channel {ctx.author.mention}.') return stats["finished"] = str(datetime.now().strftime("%d-%m-%Y %H;%M;%S")) stats["Ending Author"] = str(ctx.author) stats["Ending AuthorID"] = ctx.author.id with open(f'{PATH}\\data\\polls\\archived polls\\poll {stats["started"]} #{ctx.channel}.json','w') as f: json.dump(stats,f,indent=4) os.remove(f'{PATH}\\data\\polls\\poll{ctx.channel.id}.json') ConsoleMessage(f'{ctx.author} has ended the poll for {stats["question"]} in #{ctx.channel}') out = f'Hey @everyone!\nThe poll `{stats["question"]}` has ended!\nThe results are:' for item in stats["options"]: if item["votes"] != 0: voted = True out += f'\n\t\t`{item["name"]}` with {item["votes"]} ' if item["votes"] == 1: out += 'vote!' else: out += 'votes!' if voted: add_usage("polls ended with votes") out += '\n\nThank you everyone for voting! :smile:' else: add_usage("polls ended without votes") out += '\n\nLooks like nobody wanted to vote today... :cry:' await ctx.send(out)
async def roll_error(self, ctx, error): #will inform the console that the error is due to being used in a non-permitted channel (specified by ../config/command locations.json) if isinstance(error, commands.CheckFailure): ConsoleMessage(f'{ctx.author} attempted to use .roll in #{ctx.channel}') #otherwise, assume invalid input else: await ctx.send(f'Sorry {ctx.author.mention} but I don\'t think I understand you.\nTry typing `.roll` followed by the number of dice you would like to roll (max 30), then the dice you would like to use!\nE.g: if I wanted to roll 5, 6-sided dice, I would type `.roll 5d6`')
async def startpoll_error(self, ctx, error): if isinstance(error, commands.CheckFailure): ConsoleMessage( f'{ctx.author} failed to use .startpoll in #{ctx.channel}') else: await ctx.send( f'Sorry {ctx.author.mention}, but I don\'t think I understand you. Try .startpoll Question NumberOfVotesEach option1.+option2.+option3' )
async def pollstats_error(self, ctx, error): if isinstance(error, commands.CheckFailure): ConsoleMessage( f'{ctx.author} attempted to use .pollstats in #{ctx.channel}') else: await ctx.send( f'There doesn\'t seem to be any active polls in this channel {ctx.author.mention}. If you have any suggestions then feel free to put them in #suggestions' )
async def changestatus_error(self, ctx, error): #if the error is due to lack of privileges, inform console if isinstance(error, commands.CheckFailure): ConsoleMessage( f'{ctx.author} failed to use .changestatus due to lack of privileges' ) #otherwise, add errorlog else: ErrorLog(error)
async def vote(self, ctx, *,value): if value.lower() == 'a real number' or value.lower() == 'real number': await ctx.send(f'Sorry {ctx.author.mention}, but you need to put...ha.ha.ha.') return try: with open(f'{PATH}\\data\\polls\\poll{ctx.channel.id}.json','r') as f: stats = json.load(f) except: await ctx.send(f'There doesn\'t seem to be any active polls in this channel {ctx.author.mention}. If you have any suggestions then feel free to put them in #suggestions') return try: val = int(value) if val < 1: await ctx.send(f'Sorry {ctx.author.mention}, but I don\'t think {val} is an actual option') return except: await ctx.send(f'Sorry {ctx.author.mention}, but you need to put a real number after the `.vote `') return userVoted = False for user in stats["voters"]: if user["id"] == ctx.author.id: userVoted = True for item in user["choices"]: if item == val: await ctx.send(f'Sorry {ctx.author.mention}, but you have already voted for this!') return if len(user["choices"]) >= stats["limit"]: await ctx.send(f'Sorry {ctx.author.mention}, but it looks like you have used the maximum number of votes for this poll!') return else: try: stats["options"][val-1]["votes"] += 1 user["choices"].append(val) break except: await ctx.send(f'Sorry {ctx.author.mention}, but there isn\'t really a number {val} option.\nHere, have a look at what\'s avaliable with `.pollstats`') return if userVoted == False: try: stats["options"][val-1]["votes"] += 1 voter = {} voter["name"] = str(ctx.author) voter["id"] = ctx.author.id voter["choices"] = [val] stats["voters"].append(voter) except: await ctx.send(f'Sorry {ctx.author.mention}, but there isn\'t really a number {val} option.\nHere, have a look at what\'s avaliable with `.pollstats`') return stats["total votes"] += 1 with open(f'{PATH}\\data\\polls\\poll{ctx.channel.id}.json','w') as f: json.dump(stats,f,indent=4) add_usage("total votes") await ctx.message.delete() ConsoleMessage(f'{ctx.author} voted for "{stats["options"][val-1]["name"]}" in poll for "{stats["question"]}" on channel #{ctx.channel.name}') await ctx.send(f'Thank you for voting {ctx.author.mention}! :smile:')
async def clear_error(self, ctx, error): #if the user does not have the required permissions, inform the console if isinstance(error, commands.CheckFailure): ConsoleMessage( f'{ctx.author} failed to use .clear due to lack of privileges') #otherwise, assume poor input else: randval = random.randint(2, 20) await ctx.send( f'Sorry {ctx.author.mention}, I don\'t think I understand. Try `.clear {randval}` to remove {randval} messages' )
async def startpoll(self, ctx, question, limit, *, options): #This chunk of code is entirelly for common typos and users trying to mess with formatting question.replace('`','') question.replace('\n',' ') question.replace('\t',' ') #question = ' '.join(question) options.replace('`','') options.replace('\t',' ') options.replace('\n',' ') #options = ' '.join(options) if options.endswith('.+'): options = options[:-2] if options.endswith(' .+'): options = options[:-3] if options.startswith('.+'): options = options[2:] if options.startswith(' .+'): options = options[3:] #if the maxumum vote limit is set to 0 or below, a message is sent and the function ends if int(limit) < 1: await ctx.send(f'Hmmm...I think that people might want more than {limit} votes :sweat_smile:') return #opts = [] out = f'@everyone\nPoll started by {ctx.author.mention}:\n\n{question}' #tmp = options.split('.+') i = 1 opts = options.split('.+') for item in opts:#tmp:: if item.isspace(): del item pass out += f'\n\t`.vote {i}` for `{item}`' # opts.append(item) i += 1 if len(opts) <= 1:#tmp) <= 1: await ctx.send(f'Hmmm...I think people like more options than that') return if int(limit) > len(opts): limit = str(len(opts)) out += f'\n\n(Note: you are only allowed {limit} ' if int(limit) == 1: out += 'vote)' else: out += 'votes)' out += '\nTo see percentages for votes, type `.pollstats`\nTo see your previous votes in this poll, type `.myvotes`' if self.createpolljson(ctx, question, limit, opts): ConsoleMessage(f'{ctx.author} has created a new poll: "{question}" in #{ctx.channel.name} with options: {opts}') await ctx.message.delete() await ctx.send(out) add_usage("polls started") else: await ctx.message.delete() await ctx.send(f'Sorry {ctx.author.mention}, but there is still an ongoing poll in this channel. You can cancel it with .pollend')
async def rolemessage(self, ctx, *, msg): try: with open(f'{PATH}\\config\\roles.json', 'r') as f: data = json.load(f) data['message'] = msg with open(f'{PATH}\\config\\roles.json', 'w') as f: json.dump(data, f, indent=4) out = 'Message successfully changed!' ConsoleMessage(f'{ctx.author} changed the .rolemessage to "{msg}"') except Exception as e: ErrorLog(e) await ctx.send(out)
async def youtube(self, ctx, *, query): ConsoleMessage( f'{ctx.author} has requested for the youtube search: "{query}"') found = False for i in range(10): if found: break base = "https://www.youtube.com/results?search_query=" query = query.replace(' ', '+') #query = urllib.parse.quote(query) r = requests.get(base + query) page = r.text r.close() soup = bs(page, 'html.parser') #vids = soup.findAll('a',attrs={'class':'yt-uix-tile-link'}) vids = soup.findAll( 'a', attrs={ 'class': 'yt-simple-endpoint style-scope ytd-video-renderer' }) for v in vids: print(v) if str(v['href']).startswith('/watch?v='): result = 'https://www.youtube.com' + v['href'] found = True ConsoleMessage( f'Requested video for {ctx.author} found: "{result}" in {i+1} attempts' ) await ctx.send(result) break if not (found): print('failed to find') await ctx.send( f'Sorry {ctx.author.mention}, but I was not able to find anything :frowning:' )
async def on_raw_reaction_remove(self, ctx): #ignores bot if ctx.user_id == self.client.user.id: return if ctx.message_id == self.messageID: if ctx.emoji.name in self.roleData: server = find(lambda s: s.id == ctx.guild_id, self.client.guilds) role = get(server.roles, name=self.roleData[ctx.emoji.name]) if role != None: author = find(lambda a: a.id == ctx.user_id, server.members) await author.remove_roles(role) ConsoleMessage( f'{author} has removed their self-assigned role {self.roleData[ctx.emoji.name]}' )
async def addcmd(self, ctx, cmdName, response, error=None, desc=None, format=None, requireAdmin=False, usage=None): with open('../config/custom commands.json', 'r', encoding='UTF-8') as f: cmds = json.load(f) if cmdName.startswith('.') == False: cmdName = '.' + cmdName if cmdName in cmds: await ctx.send( f'Sorry {ctx.author.mention}, but there is already a command called `{cmdName}`' ) return if '.+' in response: response = response.split('.+') if requireAdmin.tolower() == 'true': requireAdmin = True else: requireAdmin = False newcmd = {'response': response} if (error == 'None' or error == None) and '{message}' in response: error = f'Missing required message after `{cmdName}`' if error != 'None' and error != None: newcmd['error'] = error if desc != 'None' and desc != None: newcmd['desc'] = desc if (format == 'None' or format == None) and '{message}' in response: format = f'{cmdName} [message]' if format != 'None' and format != None: newcmd['format'] = format if requireAdmin: newcmd['admin'] = True if usage != 'None' and usage != None: newcmd['usage'] = usage cmds[cmdName] = newcmd with open('../config/custom commands.json', 'w', encoding='UTF-8') as f: json.dump(cmds, f, indent=4) await ctx.send(f'Successfully created `{cmdName}` command! :smile:') ConsoleMessage(f'{ctx.author} created new command: {cmdName}')
async def changenickname(self, ctx, user, *, nickname): if int(user[3:-1]) != ctx.author.id: user = ctx.guild.get_member(int(user[3:-1])) if user != None: ConsoleMessage( f'{ctx.author} changed {user}\'s name to {nickname}') if len(nickname) > 32: await ctx.send( f'Sorry {ctx.author.mention}, but that nickname is too long (32 character limit, `{nickname}` has {len(nickname)} characters) :sweat_smile:' ) else: await user.edit(nick=nickname ) # client.change_nickname(user,nick) else: await ctx.send( f'Sorry {ctx.author.mention}, but I couldn\'t find that user :frown:\nPlease use the format of `.nick @username nickname`' ) else: await ctx.send( f'{ctx.author.mention} tried to change their own nickname, they should be ashamed! :angry:' )
async def person_16(self): while True: await asyncio.sleep(600) with open(f'{PATH}\\data\\tmpdata\\timestamps.json', 'r') as f: tmp = json.load(f)['16 personality'] if tmp != datetime.today().strftime("%Y-%m-%d"): ConsoleMessage('Posted 16 personality MOTD on channel') self.timestamp = { '16 personality': datetime.today().strftime("%Y-%m-%d"), 'Meabhs 16': self.meabh } with open(f'{PATH}\\data\\tmpdata\\timestamps.json', 'w') as f: json.dump(self.timestamp, f, indent=4) try: #moose, only1connor, spork, jallerston userlist = [ 474037926641008640, 379373007896051712, 581781390471725056, 459404973583761429 ] userimages = [] i = 1 for id in userlist: user = self.client.get_user(id) #urluser = f'https://cdn.discordapp.com/avatars/{id}/{user.avatar}.png?size=1024' #userimages.append(urluser) r = requests.get( f'https://cdn.discordapp.com/avatars/{id}/{user.avatar}.png?size=64' ) if str(r) != '<Response [404]>': with open( f'{PATH}\\resources\\images\\tmp\\avatar{i}.png', 'wb') as f: f.write(r.content) await asyncio.sleep(1) i += 1 except: pass if self.channel == None: self.channel = await self.client.fetch_channel( 738807913971318805) url = 'https://www.16personalities.com/' perstypes = ['intp', 'infj', 'infp', 'istj'] locs = [(203, 166), (612, 166), (203, 458), (612, 458)] img = Image.open( f'{PATH}\\resources\\images\\personalities.png') draw = ImageDraw.Draw(img) k = 0 for pers in perstypes: src = requests.get(f'{url}{pers}-personality').text soup = bs(src, 'html.parser') motd = soup.find('div', attrs={'class': 'insight'}) motd = str(motd)[21:-6] motd_lines = [] size = self.arial.getsize(motd)[0] motd_lines = (self.fitbox(motd)) locframe = ((8, 0), (412, 0), (8, 293), (412, 293)) if os.path.isfile( f'{PATH}\\resources\\images\\tmp\\avatar{k+1}.png' ): avatar = Image.open( f'{PATH}\\resources\\images\\tmp\\avatar{k+1}.png') frame = Image.open( f'{PATH}\\resources\\images\\persframe.png') frame.convert('RGBA') #avatar = avatar.crop((0,0,50,50)) #img.paste(avatar,(locs[k][0],locs[k][1])) #mask = Image.new("L", avatar.size, 0) #drawmask = ImageDraw.Draw(mask) #drawmask.ellipse((0, 0, avatar.size[0], avatar.size[1]), fill=255) #result = avatar.copy() #result.putalpha(mask) #result.convert('RGBA') #result.save(f'{PATH}\\resources\\images\\tmp\\av{k+1}.png') #await asyncio.sleep(1) #newav = Image.open(f'{PATH}\\resources\\images\\tmp\\av{k+1}.png') dx = 0 if k % 2 != 0: dx = 4 img.paste(frame, (locframe[k][0], locframe[k][1])) img.paste(avatar, (locs[k][0] - 32 - dx, locs[k][1] - 119)) draw.polygon( [(locframe[k][0] + 195, locframe[k][1] + 110), (locframe[k][0] + 149, locframe[k][1] + 85), (locframe[k][0] + 149, locframe[k][1] + 110)], fill=(87, 96, 113)) draw.polygon( [(locframe[k][0] + 194, locframe[k][1] + 110), (locframe[k][0] + 236, locframe[k][1] + 90), (locframe[k][0] + 236, locframe[k][1] + 110)], fill=(87, 96, 113)) os.remove( f'{PATH}\\resources\\images\\tmp\\avatar{k+1}.png') # i += 1 j = 0 for line in motd_lines: draw.text( (locs[k][0] - (self.arial.getsize(line)[0] / 2), locs[k][1] + ((self.arial.getsize('YE')[1] + 3) * j)), line, font=self.arial, fill=(255, 255, 255)) j += 1 k += 1 img.save(f'{PATH}\\resources\\images\\tmp\\tmppers.png') await self.channel.send(file=discord.File( f'{PATH}\\resources\\images\\tmp\\tmppers.png')) #await self.channel.send(f'Ignore this message, this is for testing purposes\n{urluser}') os.remove(f'{PATH}\\resources\\images\\tmp\\tmppers.png')
async def addrole(self, ctx, react, role): if react.startswith('<:') and react.endswith('>') and len(react) > 22: react = react.replace('<:', '') react = react[:-20] if role.startswith('<@&') and role.endswith('>') and len(role) == 22: role = role[3:] tmp = get(ctx.guild.roles, id=int(role[:-1])) if tmp != None: role = tmp.name out = '' rl = get(ctx.guild.roles, name=role) em = get(ctx.guild.emojis, name=react) if rl == None: out = f'I\'m sorry {ctx.author.mention} but I couldn\'t find a role called "{role}"' if em == None: if out == '': out = f'I\'m sorry {ctx.author.mention} but I couldn\'t find an emoji called ":{react}:".\nMake sure that the reaction is a custom emoji and not a default emoji' else: out += f' or an emoji called ":{react}:".\nMake sure that the reaction is a custom emoji and not a default emoji' elif out == '': try: exists = False tmprl = list(self.roleData.values()) tmpem = list(self.roleData.keys()) if role in tmprl: exists = True out = 'This role is already on the list for autoassign roles' elif react in tmpem: rlnew = get(ctx.guild.roles, name=self.roleData[react]) if rlnew != None: out = f'This reaction is already being used for the role {rlnew.mention}' exists = True if exists == False: try: with open(f'{PATH}\\config\\roles.json', 'r') as f: data = json.load(f) data['roles'].append({ "role name": role, "reaction": react }) with open(f'{PATH}\\config\\roles.json', 'w') as f: json.dump(data, f, indent=4) except: ConsoleMessage( f'{ctx.author} used .addrole but file was missing.' ) data = { 'message id': 0, 'channel id': 0, 'message': 'Hey @everyone!\nPlease react to this message if you would like access to other parts of the server with the reactions shown below! :smile:\n', 'roles': [{ "role name": role, "reaction": react }] } with open(f'{PATH}\\config\\roles.json', 'w') as f: json.dump(data, f, indent=4) ConsoleMessage( 'Missing file "config/roles.json" successfully generated' ) out = f'Successfully added the role {rl.mention} to the reaction {em}' ConsoleMessage( f'{ctx.author} added "{role}" as an assignable role with reaction ":{react}:"' ) self.roleData[react] = role except Exception as e: ErrorLog(e) await ctx.send(out)
async def endpoll_error(self,ctx,error): if isinstance(error, commands.CheckFailure): ConsoleMessage(f'{ctx.author} attempted to use .endpoll in #{ctx.channel}') else: ErrorLog(error)