<reponame>IOIIIO/starboard<filename>main.py
import re
from urllib.parse import parse_qs, urlparse

import discord
import requests
from bs4 import BeautifulSoup
from discord.ext import commands
from requests_oauthlib import OAuth1

from rogue.instagram import Instagram
from rogue.reddit import Reddit
import cogs.support.perms as perms
import cogs.support.db as dbc


class Starboard(commands.Cog, name="Starboard Commands"):
	"""Commands related to controlling the starboard."""
	def __init__(self, bot):
		self.bot = bot
		self.lockdown_mode = False
		self.db = dbc.db
		self.exceptions = dict()
		self.embed_text = "HootBot 2.0"

	# https://stackoverflow.com/a/45579374
	def get_id(self, url):
		u_pars = urlparse(url)
		quer_v = parse_qs(u_pars.query).get('v')
		if quer_v:
			return quer_v[0]
		pth = u_pars.path.split('/')
		if pth:
			return pth[-1]

	def get_server(self,id):
		return self.db['starboardServer'].find_one(server_id = id)

	def get_ignore_list(self,id):
		return self.db['starboardIgnore'].find_one(server_id = id)

	def get_custom_count(self,server_id, channel_id):
		return self.db['starboardCustom'].find_one(server_id= server_id, channel_id = channel_id)

	"""
	info = {
		'flag'
		'content'
		'image_url'
		'custom_author'
	}
	"""
	async def build_info(self,msg: discord.Message):
		info = {}

		def set_info(flag='', content='', image_url='', custom_author=''):
			info['flag'] = flag
			info['content'] = content
			info['image_url'] = image_url
			info['custom_author'] = custom_author

		# good ol' regex
		url = re.findall(
		    r"((?:https?):(?://)+(?:[\w\d_.~\-!*'();:@&=+$,/?#[\]]*))", msg.content)

		if f'{msg.guild.id}{msg.channel.id}{msg.id}' in self.exceptions:
			set_info(
				'image',
				msg.content,
				self.exceptions.pop(f'{msg.guild.id}{msg.channel.id}{msg.id}')
			)
		else:
			# tldr, someone might want to override the image
			if url and not msg.attachments:
				if 'deviantart.com' in url[0] or 'tumblr.com' in url[0] or 'pixiv.net' in url[0]:
					processed_url = requests.get(url[0].replace('mobile.', '')).text
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						BeautifulSoup(processed_url, 'html.parser').find('meta', attrs={'property': 'og:image'}).get('content')
					)
				elif 'www.instagram.com' in url[0]:
					if Instagram.return_link(url[0]):
						set_info(
							'image',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
							Instagram.return_link(url[0])
						)
					else:
						set_info(
							'message',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}'
						)
				elif 'twitter.com' in url[0]:
					# fuck twitter
					tweet_id = re.findall(r'https://twitter\.com/.*?/status/(\d*)', url[0].replace('mobile.', ''))
					r = requests.get(f'https://api.twitter.com/1.1/statuses/show.json?id={tweet_id[0]}&tweet_mode=extended', headers={"Authorization": "Bearer {}".format(dbc.ret("bot", "twitter"))}).json()
					if 'errors' not in r:
						if 'media' in r['entities']:
							set_info(
								'image',
								f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
								r['entities']['media'][0]['media_url']
							)
						else:
							set_info(
								'message',
								msg.content
							)
					else:
						set_info(
							'message',
							msg.content
						)
				elif 'reddit.com' in url[0] or 'redd.it' in url[0]:
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						Reddit.return_link(url[0])[0]
					)
				elif 'youtube.com' in url[0] or 'youtu.be' in url[0]:
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						f'https://img.youtube.com/vi/{get_id(url[0])}/0.jpg'
					)
				elif 'dcinside.com' in url[0]:
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						msg.attachments[0].url
					)
				elif 'imgur' in url[0]:
					if 'i.imgur' not in url[0]:
						processed_url = requests.get(url[0].replace('mobile.', '')).text
						set_info(
							'image',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
							BeautifulSoup(processed_url, 'html.parser').find('meta', attrs={'property': 'og:image'}).get('content').replace('?fb', '')
						)
					else:
						set_info(
							'image',
							msg.content.replace(url[0], "").strip(),
							url[0]
						)
				elif 'https://tenor.com' in url[0]:
					processed_url = requests.get(url[0].replace('mobile.', '')).text
					for img in BeautifulSoup(processed_url, 'html.parser').findAll('img', attrs={'src': True}):
						if 'media1.tenor.com' in img.get('src'):
							set_info(
								'image',
								f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
								img.get('src')
							)
				elif any(ext in url[0] for ext in ['.mp4', '.mov', '.webm']):
					set_info(
						'video',
						f'[The video below](https://youtu.be/dQw4w9WgXcQ)\n{msg.content.replace(url[0], "").strip()}',
						url[0]
					)
				elif 'discordapp.com' in url[0] or 'twimg.com' in url[0]:
					set_info(
						'image',
						msg.content.replace(url[0], '').strip(),
						msg.embeds[0].url
					)
				else:
					# high chance that it's the actual image
					if msg.embeds and msg.embeds[0].url != url[0]:
						set_info(
							'image',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
							msg.embeds[0].url
						)
					else:
						set_info(
							'message',
							msg.content,
						)
			else:
				if msg.attachments:
					file = msg.attachments[0]
					is_video = any(ext in msg.attachments[0].url for ext in ['.mp4', '.mov', '.webm'])
					set_info(
						'video' if is_video else 'image',
						f'{msg.content}\n[{"Video spoiler alert!" if is_video else "Spoiler alert!"}]({msg.attachments[0].url})' if file.is_spoiler()
							else (f'[The video below](https://youtu.be/dQw4w9WgXcQ)\n{msg.content}' if is_video else msg.content),
						'' if file.is_spoiler() else msg.attachments[0].url
					)
				else:
					if msg.embeds:
						if type(msg.embeds[0].description) != discord.embeds._EmptyEmbed:
							if any(x in msg.embeds[0].description for x in ['instagram.com', 'reddit.com', 'redd.it']):
								content = msg.embeds[0].description.split('\n')
								set_info(
									'image',
									'\n'.join(content[1:]) if len(content) > 1 else '',
									msg.embeds[0].image.__getattribute__('url'),
									await self.bot.fetch_user(msg.embeds[0].fields[0].__dict__['value'][(3 if '!' in msg.embeds[0].fields[0].__dict__['value'] else 2):len(msg.embeds[0].fields[0].__dict__['value'])-1])
								)
					else:
						set_info(
							'message',
							msg.content,
						)

		return info

	async def do_archival(self,msg: discord.Message):
		embed_info = await self.build_info(msg)
		if not embed_info:
			return

		embed = discord.Embed(color=0xffcc00)

		if embed_info['custom_author']:
			embed.set_author(name=f'{embed_info["custom_author"].display_name}', icon_url=f'{embed_info["custom_author"].avatar_url}')
		else:
			embed.set_author(name=f'{msg.author.display_name}', icon_url=f'{msg.author.avatar_url}')

		if embed_info['content']:
			content = embed_info['content'][:1020] + ('..' if len(embed_info['content']) > 1020 else '')
			embed.add_field(name='Message', value=content, inline=False)

		embed.add_field(name='Channel', value=msg.channel.mention, inline=True)
		embed.add_field(name='Message', value=f'[Jump!](https://discordapp.com/channels/{msg.guild.id}/{msg.channel.id}/{msg.id})', inline=True)

		if embed_info['flag'] == 'image' and embed_info['image_url']:
			embed.set_image(url=embed_info['image_url'])

		embed.set_footer(text=self.embed_text)


		await self.bot.get_channel(int(self.get_server(msg.guild.id)['archive_channel'])).send(embed=embed)

		if embed_info['flag'] == 'video':
			await self.bot.get_channel(int(self.get_server(msg.guild.id)['archive_channel'])).send(embed_info['image_url'])

		self.db['starboardIgnore'].insert(dict(server_id = msg.guild.id, channel_id = msg.channel.id, message_id = msg.id))

	"""
	on_raw_reaction_add is better than on_reaction_add in this case, because on_reaction_add only works with cached messages(the ones sent after the bot started).
	"""
	@commands.Cog.listener()
	async def on_raw_reaction_add(self,payload: discord.RawReactionActionEvent):
		# ignore the event if the bot isn't setup in the server
		if self.get_server(payload.guild_id) is None or self.lockdown_mode:
			return
		try:
			msg: discord.Message = await self.bot.get_channel(payload.channel_id).fetch_message(payload.message_id)
		except:
			print("Starboard - Message 404")
			return

		if self.db['starboardIgnore'].find_one(server_id = payload.guild_id, channel_id = payload.channel_id, message_id = payload.message_id) is not None:
			return

		emote_match = list(filter(lambda r: str(r) == self.get_server(payload.guild_id)['archive_emote'], msg.reactions))
		channel_count = self.db['starboardCustom'].find_one(server_id = payload.guild_id, channel_id = payload.channel_id)
		needed_count = channel_count['amount'] if channel_count is not None else self.get_server(payload.guild_id)['archive_emote_amount']
		if emote_match and emote_match[0].count >= needed_count:
			await self.do_archival(msg)

	@commands.group()
	@perms.mod()
	async def starboard(self, ctx):
		"""Generic container for embed commands. run <p>help starboard for more info."""
		return

	"""
	Setups the bot.
	"""
	@starboard.command(brief = 'Setups the bot for the server.')
	@perms.mod()
	async def setup(self,ctx: commands.Context, archive_channel: discord.TextChannel, archive_emote, archive_emote_amount: int):
		if self.get_server(ctx.guild.id) is not None:
			await ctx.send('Bot has been setup already.')
			return

		self.db['starboardServer'].insert(dict(
			server_id = ctx.guild.id,
			archive_channel = archive_channel.id,
			archive_emote = str(archive_emote),
			archive_emote_amount = archive_emote_amount,
			reddit_embed = True,
			instagram_embed = True
		))

	@starboard.command(brief = 'Removes the given message from the archive cache.')
	@perms.mod()
	async def del_entry(self,ctx: commands.Context, msg_id: str):
		if self.get_server(ctx.guild.id) is None:
			return

		self.db['starboardIgnore'].delete(server_id = ctx.guild.id, channel_id = ctx.channel.id, message_id = msg_id)

	@starboard.command(brief = 'Overrides archive images before archival.')
	@perms.mod()
	async def override(self,ctx: commands.Context, msg_id: str, link):
		if self.get_server(ctx.guild.id) is None:
			return

		self.exceptions[str(ctx.guild.id) + str(ctx.channel.id) + msg_id] = link

		await ctx.message.delete()

	@starboard.command(brief = 'Used for reloading embeds.')
	@perms.mod()
	async def reload(self,ctx: commands.Context, msg_id: int):
		if self.get_server(ctx.guild.id) is None:
			return

		channel: discord.TextChannel = bot.get_channel(int(self.get_server(ctx.guild.id)['archive_channel']))
		msg: discord.Message = await channel.fetch_message(msg_id)
		embed: discord.Embed = msg.embeds[0]
		await msg.delete()
		await channel.send(embed=embed)

	@starboard.command(brief='Sets the archive channel.', aliases=['sch'])
	@perms.mod()
	async def set_channel(self,ctx: commands.Context, value: discord.TextChannel):
		if self.get_server(ctx.guild.id) is None:
			return

		self.db['starboardServer'].update(dict(server_id=str(ctx.guild.id), archive_channel=value.id), ['server_id'])
		await ctx.channel.send('Set channel to ' + f'<#{bot.get_channel(self.get_server(ctx.guild.id)["archive_channel"]).id}>')

	@starboard.command(brief='Sets the amount of emotes necessary for archival.', aliases=['samt'])
	@perms.mod()
	async def set_amount(self,ctx: commands.Context, value: int):
		if self.get_server(ctx.guild.id) is None:
			return

		self.db['starboardServer'].update(dict(server_id=str(ctx.guild.id), archive_emote_amount=value), ['server_id'])
		await ctx.channel.send('Set necessary amount of ' + self.get_server(ctx.guild.id)['archive_emote'] + ' to ' + str(self.get_server(ctx.guild.id)['archive_emote_amount']))

	@starboard.command(brief='Sets the amount of emotes necessary for archival.', aliases=['scamt'])
	@perms.mod()
	async def set_channel_amount(self,ctx: commands.Context, channel: discord.TextChannel, value: int):
		if self.get_server(ctx.guild.id) is None:
			return

		if self.get_custom_count(ctx.guild.id, channel.id) is None:
			self.db['starboardCustom'].insert(dict(server_id = ctx.guild.id, channel_id = channel.id, amount = value))
		else:
			self.db['starboardCustom'].update(dict(server_id = ctx.guild.id, channel_id = channel.id, amount = value), ['server_id', 'channel_id'])

		await ctx.channel.send('Set necessary amount of ' + self.get_server(ctx.guild.id)['archive_emote'] + ' in ' + f'<#{self.get_custom_count(ctx.guild.id, channel.id)["channel_id"]}>' + ' to ' + str(self.get_custom_count(ctx.guild.id, channel.id)['amount']))
	
	@starboard.command()
	@commands.is_owner()
	async def twitter(self, ctx, bearer_token: str):
		"""Setup the twitter bearer for twitter support."""
		try:
			dbc.save("bot", "twitter", bearer_token)
			await ctx.send("Successfully set Twitter token!")
			return
		except:
			await ctx.send("Failed to set token.")
			return

def setup(bot):
	bot.add_cog(Starboard(bot))

import re
from urllib.parse import parse_qs, urlparse

import discord
import requests
from bs4 import BeautifulSoup
from discord.ext import commands
from requests_oauthlib import OAuth1

from rogue.instagram import Instagram
from rogue.reddit import Reddit
import cogs.support.perms as perms
import cogs.support.db as dbc


class Starboard(commands.Cog, name="Starboard Commands"):
	"""Commands related to controlling the starboard."""
	def __init__(self, bot):
		self.bot = bot
		self.lockdown_mode = False
		self.db = dbc.db
		self.exceptions = dict()
		self.embed_text = "HootBot 2.0"

	# https://stackoverflow.com/a/45579374
	def get_id(self, url):
		u_pars = urlparse(url)
		quer_v = parse_qs(u_pars.query).get('v')
		if quer_v:
			return quer_v[0]
		pth = u_pars.path.split('/')
		if pth:
			return pth[-1]

	def get_server(self,id):
		return self.db['starboardServer'].find_one(server_id = id)

	def get_ignore_list(self,id):
		return self.db['starboardIgnore'].find_one(server_id = id)

	def get_custom_count(self,server_id, channel_id):
		return self.db['starboardCustom'].find_one(server_id= server_id, channel_id = channel_id)

	"""
	info = {
		'flag'
		'content'
		'image_url'
		'custom_author'
	}
	"""
	async def build_info(self,msg: discord.Message):
		info = {}

		def set_info(flag='', content='', image_url='', custom_author=''):
			info['flag'] = flag
			info['content'] = content
			info['image_url'] = image_url
			info['custom_author'] = custom_author

		# good ol' regex
		url = re.findall(
		    r"((?:https?):(?://)+(?:[\w\d_.~\-!*'();:@&=+$,/?#[\]]*))", msg.content)

		if f'{msg.guild.id}{msg.channel.id}{msg.id}' in self.exceptions:
			set_info(
				'image',
				msg.content,
				self.exceptions.pop(f'{msg.guild.id}{msg.channel.id}{msg.id}')
			)
		else:
			# tldr, someone might want to override the image
			if url and not msg.attachments:
				if 'deviantart.com' in url[0] or 'tumblr.com' in url[0] or 'pixiv.net' in url[0]:
					processed_url = requests.get(url[0].replace('mobile.', '')).text
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						BeautifulSoup(processed_url, 'html.parser').find('meta', attrs={'property': 'og:image'}).get('content')
					)
				elif 'www.instagram.com' in url[0]:
					if Instagram.return_link(url[0]):
						set_info(
							'image',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
							Instagram.return_link(url[0])
						)
					else:
						set_info(
							'message',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}'
						)
				elif 'twitter.com' in url[0]:
					# fuck twitter
					tweet_id = re.findall(r'https://twitter\.com/.*?/status/(\d*)', url[0].replace('mobile.', ''))
					r = requests.get(f'https://api.twitter.com/1.1/statuses/show.json?id={tweet_id[0]}&tweet_mode=extended', headers={"Authorization": "Bearer {}".format(dbc.ret("bot", "twitter"))}).json()
					if 'errors' not in r:
						if 'media' in r['entities']:
							set_info(
								'image',
								f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
								r['entities']['media'][0]['media_url']
							)
						else:
							set_info(
								'message',
								msg.content
							)
					else:
						set_info(
							'message',
							msg.content
						)
				elif 'reddit.com' in url[0] or 'redd.it' in url[0]:
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						Reddit.return_link(url[0])[0]
					)
				elif 'youtube.com' in url[0] or 'youtu.be' in url[0]:
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						f'https://img.youtube.com/vi/{get_id(url[0])}/0.jpg'
					)
				elif 'dcinside.com' in url[0]:
					set_info(
						'image',
						f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
						msg.attachments[0].url
					)
				elif 'imgur' in url[0]:
					if 'i.imgur' not in url[0]:
						processed_url = requests.get(url[0].replace('mobile.', '')).text
						set_info(
							'image',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
							BeautifulSoup(processed_url, 'html.parser').find('meta', attrs={'property': 'og:image'}).get('content').replace('?fb', '')
						)
					else:
						set_info(
							'image',
							msg.content.replace(url[0], "").strip(),
							url[0]
						)
				elif 'https://tenor.com' in url[0]:
					processed_url = requests.get(url[0].replace('mobile.', '')).text
					for img in BeautifulSoup(processed_url, 'html.parser').findAll('img', attrs={'src': True}):
						if 'media1.tenor.com' in img.get('src'):
							set_info(
								'image',
								f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
								img.get('src')
							)
				elif any(ext in url[0] for ext in ['.mp4', '.mov', '.webm']):
					set_info(
						'video',
						f'[The video below](https://youtu.be/dQw4w9WgXcQ)\n{msg.content.replace(url[0], "").strip()}',
						url[0]
					)
				elif 'discordapp.com' in url[0] or 'twimg.com' in url[0]:
					set_info(
						'image',
						msg.content.replace(url[0], '').strip(),
						msg.embeds[0].url
					)
				else:
					# high chance that it's the actual image
					if msg.embeds and msg.embeds[0].url != url[0]:
						set_info(
							'image',
							f'[Source]({url[0]})\n{msg.content.replace(url[0], "").strip()}',
							msg.embeds[0].url
						)
					else:
						set_info(
							'message',
							msg.content,
						)
			else:
				if msg.attachments:
					file = msg.attachments[0]
					is_video = any(ext in msg.attachments[0].url for ext in ['.mp4', '.mov', '.webm'])
					set_info(
						'video' if is_video else 'image',
						f'{msg.content}\n[{"Video spoiler alert!" if is_video else "Spoiler alert!"}]({msg.attachments[0].url})' if file.is_spoiler()
							else (f'[The video below](https://youtu.be/dQw4w9WgXcQ)\n{msg.content}' if is_video else msg.content),
						'' if file.is_spoiler() else msg.attachments[0].url
					)
				else:
					if msg.embeds:
						if type(msg.embeds[0].description) != discord.embeds._EmptyEmbed:
							if any(x in msg.embeds[0].description for x in ['instagram.com', 'reddit.com', 'redd.it']):
								content = msg.embeds[0].description.split('\n')
								set_info(
									'image',
									'\n'.join(content[1:]) if len(content) > 1 else '',
									msg.embeds[0].image.__getattribute__('url'),
									await self.bot.fetch_user(msg.embeds[0].fields[0].__dict__['value'][(3 if '!' in msg.embeds[0].fields[0].__dict__['value'] else 2):len(msg.embeds[0].fields[0].__dict__['value'])-1])
								)
					else:
						set_info(
							'message',
							msg.content,
						)

		return info

	async def do_archival(self,msg: discord.Message):
		embed_info = await self.build_info(msg)
		if not embed_info:
			return

		embed = discord.Embed(color=0xffcc00)

		if embed_info['custom_author']:
			embed.set_author(name=f'{embed_info["custom_author"].display_name}', icon_url=f'{embed_info["custom_author"].avatar_url}')
		else:
			embed.set_author(name=f'{msg.author.display_name}', icon_url=f'{msg.author.avatar_url}')

		if embed_info['content']:
			content = embed_info['content'][:1020] + ('..' if len(embed_info['content']) > 1020 else '')
			embed.add_field(name='Message', value=content, inline=False)

		embed.add_field(name='Channel', value=msg.channel.mention, inline=True)
		embed.add_field(name='Message', value=f'[Jump!](https://discordapp.com/channels/{msg.guild.id}/{msg.channel.id}/{msg.id})', inline=True)

		if embed_info['flag'] == 'image' and embed_info['image_url']:
			embed.set_image(url=embed_info['image_url'])

		embed.set_footer(text=self.embed_text)


		await self.bot.get_channel(int(self.get_server(msg.guild.id)['archive_channel'])).send(embed=embed)

		if embed_info['flag'] == 'video':
			await self.bot.get_channel(int(self.get_server(msg.guild.id)['archive_channel'])).send(embed_info['image_url'])

		self.db['starboardIgnore'].insert(dict(server_id = msg.guild.id, channel_id = msg.channel.id, message_id = msg.id))

	"""
	on_raw_reaction_add is better than on_reaction_add in this case, because on_reaction_add only works with cached messages(the ones sent after the bot started).
	"""
	@commands.Cog.listener()
	async def on_raw_reaction_add(self,payload: discord.RawReactionActionEvent):
		# ignore the event if the bot isn't setup in the server
		if self.get_server(payload.guild_id) is None or self.lockdown_mode:
			return
		try:
			msg: discord.Message = await self.bot.get_channel(payload.channel_id).fetch_message(payload.message_id)
		except:
			print("Starboard - Message 404")
			return

		if self.db['starboardIgnore'].find_one(server_id = payload.guild_id, channel_id = payload.channel_id, message_id = payload.message_id) is not None:
			return

		emote_match = list(filter(lambda r: str(r) == self.get_server(payload.guild_id)['archive_emote'], msg.reactions))
		channel_count = self.db['starboardCustom'].find_one(server_id = payload.guild_id, channel_id = payload.channel_id)
		needed_count = channel_count['amount'] if channel_count is not None else self.get_server(payload.guild_id)['archive_emote_amount']
		if emote_match and emote_match[0].count >= needed_count:
			await self.do_archival(msg)

	@commands.group()
	@perms.mod()
	async def starboard(self, ctx):
		"""Generic container for embed commands. run <p>help starboard for more info."""
		return

	"""
	Setups the bot.
	"""
	@starboard.command(brief = 'Setups the bot for the server.')
	@perms.mod()
	async def setup(self,ctx: commands.Context, archive_channel: discord.TextChannel, archive_emote, archive_emote_amount: int):
		if self.get_server(ctx.guild.id) is not None:
			await ctx.send('Bot has been setup already.')
			return

		self.db['starboardServer'].insert(dict(
			server_id = ctx.guild.id,
			archive_channel = archive_channel.id,
			archive_emote = str(archive_emote),
			archive_emote_amount = archive_emote_amount,
			reddit_embed = True,
			instagram_embed = True
		))

	@starboard.command(brief = 'Removes the given message from the archive cache.')
	@perms.mod()
	async def del_entry(self,ctx: commands.Context, msg_id: str):
		if self.get_server(ctx.guild.id) is None:
			return

		self.db['starboardIgnore'].delete(server_id = ctx.guild.id, channel_id = ctx.channel.id, message_id = msg_id)

	@starboard.command(brief = 'Overrides archive images before archival.')
	@perms.mod()
	async def override(self,ctx: commands.Context, msg_id: str, link):
		if self.get_server(ctx.guild.id) is None:
			return

		self.exceptions[str(ctx.guild.id) + str(ctx.channel.id) + msg_id] = link

		await ctx.message.delete()

	@starboard.command(brief = 'Used for reloading embeds.')
	@perms.mod()
	async def reload(self,ctx: commands.Context, msg_id: int):
		if self.get_server(ctx.guild.id) is None:
			return

		channel: discord.TextChannel = bot.get_channel(int(self.get_server(ctx.guild.id)['archive_channel']))
		msg: discord.Message = await channel.fetch_message(msg_id)
		embed: discord.Embed = msg.embeds[0]
		await msg.delete()
		await channel.send(embed=embed)

	@starboard.command(brief='Sets the archive channel.', aliases=['sch'])
	@perms.mod()
	async def set_channel(self,ctx: commands.Context, value: discord.TextChannel):
		if self.get_server(ctx.guild.id) is None:
			return

		self.db['starboardServer'].update(dict(server_id=str(ctx.guild.id), archive_channel=value.id), ['server_id'])
		await ctx.channel.send('Set channel to ' + f'<#{bot.get_channel(self.get_server(ctx.guild.id)["archive_channel"]).id}>')

	@starboard.command(brief='Sets the amount of emotes necessary for archival.', aliases=['samt'])
	@perms.mod()
	async def set_amount(self,ctx: commands.Context, value: int):
		if self.get_server(ctx.guild.id) is None:
			return

		self.db['starboardServer'].update(dict(server_id=str(ctx.guild.id), archive_emote_amount=value), ['server_id'])
		await ctx.channel.send('Set necessary amount of ' + self.get_server(ctx.guild.id)['archive_emote'] + ' to ' + str(self.get_server(ctx.guild.id)['archive_emote_amount']))

	@starboard.command(brief='Sets the amount of emotes necessary for archival.', aliases=['scamt'])
	@perms.mod()
	async def set_channel_amount(self,ctx: commands.Context, channel: discord.TextChannel, value: int):
		if self.get_server(ctx.guild.id) is None:
			return

		if self.get_custom_count(ctx.guild.id, channel.id) is None:
			self.db['starboardCustom'].insert(dict(server_id = ctx.guild.id, channel_id = channel.id, amount = value))
		else:
			self.db['starboardCustom'].update(dict(server_id = ctx.guild.id, channel_id = channel.id, amount = value), ['server_id', 'channel_id'])

		await ctx.channel.send('Set necessary amount of ' + self.get_server(ctx.guild.id)['archive_emote'] + ' in ' + f'<#{self.get_custom_count(ctx.guild.id, channel.id)["channel_id"]}>' + ' to ' + str(self.get_custom_count(ctx.guild.id, channel.id)['amount']))
	
	@starboard.command()
	@commands.is_owner()
	async def twitter(self, ctx, bearer_token: str):
		"""Setup the twitter bearer for twitter support."""
		try:
			dbc.save("bot", "twitter", bearer_token)
			await ctx.send("Successfully set Twitter token!")
			return
		except:
			await ctx.send("Failed to set token.")
			return

def setup(bot):
	bot.add_cog(Starboard(bot))
Commands related to controlling the starboard. # https://stackoverflow.com/a/45579374 info = {
 		'flag'
 		'content'
 		'image_url'
 		'custom_author'
 	} # good ol' regex #[\]]*))", msg.content) # tldr, someone might want to override the image # fuck twitter # high chance that it's the actual image on_raw_reaction_add is better than on_reaction_add in this case, because on_reaction_add only works with cached messages(the ones sent after the bot started). # ignore the event if the bot isn't setup in the server Generic container for embed commands. run <p>help starboard for more info. Setups the bot. #{bot.get_channel(self.get_server(ctx.guild.id)["archive_channel"]).id}>') #{self.get_custom_count(ctx.guild.id, channel.id)["channel_id"]}>' + ' to ' + str(self.get_custom_count(ctx.guild.id, channel.id)['amount'])) Setup the twitter bearer for twitter support.