Advertisement
Guest User

Untitled

a guest
Nov 23rd, 2017
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.91 KB | None | 0 0
  1. '''
  2. MIT License
  3. Copyright (c) 2017 ManGo
  4. '''
  5.  
  6. GUILD_ID = 0 # your guild id here
  7.  
  8. import discord
  9. from discord.ext import commands
  10. from urllib.parse import urlparse
  11. from random import choice, randint
  12. import asyncio
  13. import textwrap
  14. import datetime
  15. import time
  16. import json
  17. import sys
  18. import os
  19. import re
  20. import string
  21. import importlib
  22. import traceback
  23. import logging
  24. import asyncio
  25. import threading
  26. import datetime
  27. import glob
  28. import os
  29. import aiohttp
  30. import time
  31.  
  32. class Modmail(commands.Bot):
  33. def __init__(self):
  34. super().__init__(command_prefix=self.get_pre)
  35. self.uptime = datetime.datetime.utcnow()
  36. self._add_commands()
  37.  
  38. def _add_commands(self):
  39. '''Adds commands automatically'''
  40. for attr in dir(self):
  41. cmd = getattr(self, attr)
  42. if isinstance(cmd, commands.Command):
  43. self.add_command(cmd)
  44.  
  45. @property
  46. def token(self):
  47. '''Returns your token wherever it is'''
  48. try:
  49. with open('config.json') as f:
  50. config = json.load(f)
  51. if config.get('TOKEN') == "your_token_here":
  52. if not os.environ.get('TOKEN'):
  53. self.run_wizard()
  54. else:
  55. token = config.get('TOKEN').strip('\"')
  56. except FileNotFoundError:
  57. token = None
  58. return os.environ.get('TOKEN') or token
  59.  
  60. @staticmethod
  61. async def get_pre(bot, message):
  62. '''Returns the prefix.'''
  63. with open('config.json') as f:
  64. prefix = json.load(f).get('PREFIX')
  65. return os.environ.get('PREFIX') or prefix or 'm.'
  66.  
  67. @staticmethod
  68. def run_wizard():
  69. '''Wizard for first start'''
  70. print('------------------------------------------')
  71. token = input('Enter your token:\n> ')
  72. print('------------------------------------------')
  73. data = {
  74. "TOKEN" : token,
  75. }
  76. with open('data/config.json','w') as f:
  77. f.write(json.dumps(data, indent=4))
  78. print('------------------------------------------')
  79. print('Restarting...')
  80. print('------------------------------------------')
  81. os.execv(sys.executable, ['python'] + sys.argv)
  82.  
  83. @classmethod
  84. def init(cls, token=None):
  85. '''Starts the actual bot'''
  86. bot = cls()
  87. if token:
  88. to_use = token.strip('"')
  89. else:
  90. to_use = bot.token.strip('"')
  91. try:
  92. bot.run(to_use, reconnect=True)
  93. except Exception as e:
  94. raise e
  95.  
  96. async def on_connect(self):
  97. print('---------------')
  98. print('Modmail connected!')
  99.  
  100. @property
  101. def guild_id(self):
  102. from_heroku = os.environ.get('GUILD_ID')
  103. return int(from_heroku) if from_heroku else GUILD_ID
  104.  
  105. async def on_ready(self):
  106. '''Bot startup, sets uptime.'''
  107. self.guild = discord.utils.get(self.guilds, id=self.guild_id)
  108. print(textwrap.dedent(f'''
  109. ---------------
  110. Client is ready!
  111. ---------------
  112. Author: ManGo#7532
  113. ---------------
  114. Logged in as: {self.user}
  115. User ID: {self.user.id}
  116. ---------------
  117. '''))
  118.  
  119. def overwrites(self, ctx, modrole=None):
  120. '''Permision overwrites for the guild.'''
  121. overwrites = {
  122. ctx.guild.default_role: discord.PermissionOverwrite(read_messages=False)
  123. }
  124.  
  125. if modrole:
  126. overwrites[modrole] = discord.PermissionOverwrite(read_messages=True)
  127. else:
  128. for role in self.guess_modroles(ctx):
  129. overwrites[role] = discord.PermissionOverwrite(read_messages=True)
  130.  
  131. return overwrites
  132.  
  133. def help_embed(self):
  134. em = discord.Embed(color=0x00FFFF)
  135. em.set_author(name='Mod Mail - Help', icon_url=self.user.avatar_url)
  136. em.description = 'This bot is a python implementation by **ManGo**'
  137.  
  138.  
  139. cmds = '`m.setup [modrole] <- (optional)` - Command that sets up the bot.\n' \
  140. '`m.reply <message...>` - Sends a message to the current thread\'s recipient.\n' \
  141. '`m.close` - Closes the current thread and deletes the channel.\n' \
  142. '`m.disable` - Closes all threads and disables modmail for the server.\n' \
  143. '`m.customstatus` - Sets the Bot status to whatever you want.'
  144.  
  145. warn = 'Do not manually delete the category or channels as it will break the system. ' \
  146. 'Modifying the channel topic will also break the system.'
  147. em.add_field(name='Commands', value=cmds)
  148. em.add_field(name='Warning', value=warn)
  149. em.add_field(name='Owner ManGo', value='ManGo#7532')
  150. em.set_footer(text='Star the repository to unlock hidden features!')
  151.  
  152. return em
  153.  
  154. @commands.command()
  155. @commands.has_permissions(administrator=True)
  156. async def setup(self, ctx, *, modrole: discord.Role=None):
  157. '''Sets up a server for modmail'''
  158. if discord.utils.get(ctx.guild.categories, name='Mod Mail'):
  159. return await ctx.send('This server is already set up.')
  160.  
  161. categ = await ctx.guild.create_category(
  162. name='Mod Mail',
  163. overwrites=self.overwrites(ctx, modrole=modrole)
  164. )
  165. await categ.edit(position=0)
  166. c = await ctx.guild.create_text_channel(name='bot-info', category=categ)
  167. await c.edit(topic='Manually add user id\'s to block users.\n\n'
  168. 'Blocked\n-------\n\n')
  169. await c.send(embed=self.help_embed())
  170. await ctx.send('Successfully set up server.')
  171.  
  172. @commands.command()
  173. @commands.has_permissions(administrator=True)
  174. async def disable(self, ctx):
  175. '''Close all threads and disable modmail.'''
  176. categ = discord.utils.get(ctx.guild.categories, name='Mod Mail')
  177. if not categ:
  178. return await ctx.send('This server is not set up.')
  179. for category, channels in ctx.guild.by_category():
  180. if category == categ:
  181. for chan in channels:
  182. if 'User ID:' in str(chan.topic):
  183. user_id = int(chan.topic.split(': ')[1])
  184. user = self.get_user(user_id)
  185. await user.send(f'**{ctx.author}** has closed this modmail session.')
  186. await chan.delete()
  187. await categ.delete()
  188. await ctx.send('Disabled modmail.')
  189.  
  190.  
  191. @commands.command(name='close')
  192. @commands.has_permissions(manage_guild=True)
  193. async def _close(self, ctx):
  194. '''Close the current thread.'''
  195. if 'User ID:' not in str(ctx.channel.topic):
  196. return await ctx.send('This is not a modmail thread.')
  197. user_id = int(ctx.channel.topic.split(': ')[1])
  198. user = self.get_user(user_id)
  199. em = discord.Embed(title='Thread Closed')
  200. em.description = f'**{ctx.author}** has closed this modmail session.'
  201. em.color = discord.Color.red()
  202. try:
  203. await user.send(embed=em)
  204. except:
  205. pass
  206. await ctx.channel.delete()
  207.  
  208. @commands.command()
  209. async def ping(self, ctx):
  210. """Pong! Returns your websocket latency."""
  211. em = discord.Embed()
  212. em.title ='Pong! Websocket Latency:'
  213. em.description = f'{self.ws.latency * 1000:.4f} ms'
  214. em.color = 0x00FF00
  215. await ctx.send(embed=em)
  216.  
  217. def guess_modroles(self, ctx):
  218. '''Finds roles if it has the manage_guild perm'''
  219. for role in ctx.guild.roles:
  220. if role.permissions.manage_guild:
  221. yield role
  222.  
  223. def format_info(self, user):
  224. '''Get information about a member of a server
  225. supports users from the guild or not.'''
  226. server = self.guild
  227. member = self.guild.get_member(user.id)
  228. avi = user.avatar_url
  229. time = datetime.datetime.utcnow()
  230. desc = 'Modmail thread started.'
  231. color = 0
  232.  
  233. if member:
  234. roles = sorted(member.roles, key=lambda c: c.position)
  235. rolenames = ', '.join([r.name for r in roles if r.name != "@everyone"]) or 'None'
  236. member_number = sorted(server.members, key=lambda m: m.joined_at).index(member) + 1
  237. for role in roles:
  238. if str(role.color) != "#000000":
  239. color = role.color
  240.  
  241. em = discord.Embed(colour=color, description=desc, timestamp=time)
  242.  
  243. em.add_field(name='Account Created', value=str((time - user.created_at).days)+' days ago.')
  244. em.set_footer(text='User ID: '+str(user.id))
  245. em.set_thumbnail(url=avi)
  246. em.set_author(name=user, icon_url=server.icon_url)
  247.  
  248. if member:
  249. em.add_field(name='Joined', value=str((time - member.joined_at).days)+' days ago.')
  250. em.add_field(name='Member No.',value=str(member_number),inline = True)
  251. em.add_field(name='Nick', value=member.nick, inline=True)
  252. em.add_field(name='Roles', value=rolenames, inline=True)
  253.  
  254. return em
  255.  
  256. async def send_mail(self, message, channel, mod):
  257. author = message.author
  258. fmt = discord.Embed()
  259. fmt.description = message.content
  260. fmt.timestamp = message.created_at
  261.  
  262. urls = re.findall(r'(https?://[^\s]+)', message.content)
  263.  
  264. types = ['.png', '.jpg', '.gif', '.jpeg', '.webp']
  265.  
  266. for u in urls:
  267. if any(urlparse(u).path.endswith(x) for x in types):
  268. fmt.set_image(url=u)
  269. break
  270.  
  271. if mod:
  272. fmt.color=discord.Color.green()
  273. fmt.set_author(name=str(author), icon_url=author.avatar_url)
  274. fmt.set_footer(text='Moderator')
  275. else:
  276. fmt.color=discord.Color.gold()
  277. fmt.set_author(name=str(author), icon_url=author.avatar_url)
  278. fmt.set_footer(text='User')
  279.  
  280. embed = None
  281.  
  282. if message.attachments:
  283. fmt.set_image(url=message.attachments[0].url)
  284.  
  285. await channel.send(embed=fmt)
  286.  
  287. async def process_reply(self, message):
  288. try:
  289. await message.delete()
  290. except discord.errors.NotFound:
  291. pass
  292. await self.send_mail(message, message.channel, mod=True)
  293. user_id = int(message.channel.topic.split(': ')[1])
  294. user = self.get_user(user_id)
  295. await self.send_mail(message, user, mod=True)
  296.  
  297. def format_name(self, author):
  298. name = author.name
  299. new_name = ''
  300. for letter in name:
  301. if letter in string.ascii_letters + string.digits:
  302. new_name += letter
  303. if not new_name:
  304. new_name = 'null'
  305. new_name += f'-{author.discriminator}'
  306. return new_name
  307.  
  308. @property
  309. def blocked_em(self):
  310. em = discord.Embed(title='Message not sent!', color=discord.Color.red())
  311. em.description = 'You have been blocked from using modmail.'
  312. return em
  313.  
  314. async def process_modmail(self, message):
  315. '''Processes messages sent to the bot.'''
  316. try:
  317. await message.add_reaction('✅')
  318. except:
  319. pass
  320.  
  321. guild = self.guild
  322. author = message.author
  323. topic = f'User ID: {author.id}'
  324. channel = discord.utils.get(guild.text_channels, topic=topic)
  325. categ = discord.utils.get(guild.categories, name='Mod Mail')
  326. top_chan = categ.channels[0] #bot-info
  327. blocked = top_chan.topic.split('Blocked\n-------')[1].strip().split('\n')
  328. blocked = [x.strip() for x in blocked]
  329.  
  330. if str(message.author.id) in blocked:
  331. return await message.author.send(embed=self.blocked_em)
  332.  
  333. em = discord.Embed(title='Thanks for the message!')
  334. em.description = 'The moderation team will get back to you as soon as possible!'
  335. em.color = discord.Color.green()
  336.  
  337. if channel is not None:
  338. await self.send_mail(message, channel, mod=False)
  339. else:
  340. await message.author.send(embed=em)
  341. channel = await guild.create_text_channel(
  342. name=self.format_name(author),
  343. category=categ
  344. )
  345. await channel.edit(topic=topic)
  346. await channel.send('@here', embed=self.format_info(author))
  347. await channel.send('\u200b')
  348. await self.send_mail(message, channel, mod=False)
  349.  
  350. async def on_message(self, message):
  351. if message.author.bot:
  352. return
  353. await self.process_commands(message)
  354. if isinstance(message.channel, discord.DMChannel):
  355. await self.process_modmail(message)
  356.  
  357. @commands.command()
  358. async def reply(self, ctx, *, msg):
  359. '''Reply to users using this command.'''
  360. categ = discord.utils.get(ctx.guild.categories, id=ctx.channel.category_id)
  361. if categ is not None:
  362. if categ.name == 'Mod Mail':
  363. if 'User ID:' in ctx.channel.topic:
  364. ctx.message.content = msg
  365. await self.process_reply(ctx.message)
  366.  
  367. @commands.command(name="customstatus", aliases=['status', 'presence'])
  368. @commands.has_permissions(administrator=True)
  369. async def _status(self, ctx, *, message):
  370. '''Set a custom playing status for the bot.'''
  371. if message == 'clear':
  372. return await self.change_presence(game=None)
  373. await self.change_presence(game=discord.Game(name=message), status=discord.Status.idle)
  374. await ctx.send(f"Changed status to **{message}**")
  375.  
  376. @commands.command()
  377. @commands.has_permissions(manage_guild=True)
  378. async def block(self, ctx, id=None):
  379. '''Block a user from using modmail.'''
  380. if id is None:
  381. if 'User ID:' in str(ctx.channel.topic):
  382. id = ctx.channel.topic.split('User ID: ')[1].strip()
  383. else:
  384. return await ctx.send('No User ID provided.')
  385.  
  386. categ = discord.utils.get(ctx.guild.categories, name='Mod Mail')
  387. top_chan = categ.channels[0] #bot-info
  388. topic = str(top_chan.topic)
  389. topic += id + '\n'
  390.  
  391. if id not in top_chan.topic:
  392. await top_chan.edit(topic=topic)
  393. await ctx.send('User successfully blocked!')
  394. else:
  395. await ctx.send('User is already blocked.')
  396.  
  397. @commands.command()
  398. @commands.has_permissions(administrator=True)
  399. async def unblock(self, ctx, id=None):
  400. '''Unblocks a user from using modmail.'''
  401. if id is None:
  402. if 'User ID:' in str(ctx.channel.topic):
  403. id = ctx.channel.topic.split('User ID: ')[1].strip()
  404. else:
  405. return await ctx.send('No User ID provided.')
  406.  
  407. categ = discord.utils.get(ctx.guild.categories, name='Mod Mail')
  408. top_chan = categ.channels[0] #bot-info
  409. topic = str(top_chan.topic)
  410. topic = topic.replace(id+'\n', '')
  411.  
  412. if id in top_chan.topic:
  413. await top_chan.edit(topic=topic)
  414. await ctx.send('User successfully unblocked!')
  415. else:
  416. await ctx.send('User is not already blocked.')
  417.  
  418. @commands.command()
  419. @commands.has_permissions(administrator=True)
  420. async def rainbow(self, ctx, interval:float, *, role):
  421. roleObj = discord.utils.find(lambda r: r.name == role, ctx.message.guild.roles)
  422. if not roleObj:
  423. no = discord.Embed(title="{} is not a valid role".format(role))
  424. await self.say(embed=no)
  425. return
  426. if interval < 3:
  427. interval = 3
  428. while True:
  429. colour = ''.join([choice('0123456789ABCDEF') for x in range(6)])
  430. colour = int(colour, 16)
  431. await self.edit_role(ctx.message.guild, roleObj, colour=discord.Colour(value=colour))
  432. await asyncio.sleep(interval)
  433.  
  434. if __name__ == '__main__':
  435. Modmail.init()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement