<reponame>TBTerra/roxbot<filename>roxbot/cogs/reddit.py
# -*- coding: utf-8 -*-

"""
MIT License

Copyright (c) 2017-2018 <NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""


import random
import roxbot
from html import unescape
from bs4 import BeautifulSoup
from roxbot import guild_settings
from discord.ext import commands


async def _imgur_removed(url):
	page = await roxbot.http.get_page(url)
	soup = BeautifulSoup(page, 'html.parser')
	return bool("removed.png" in soup.img["src"])


async def imgur_get(url):
	if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv"):
		return url
	else:
		if await _imgur_removed(url):
			return False

		base_endpoint = "https://api.imgur.com/3/"
		endpoint_album = base_endpoint + "album/{}/images.json".format(url.split("/")[-1])
		endpoint_image = base_endpoint + "image/{}.json".format(url.split("/")[-1])

		resp = await roxbot.http.api_request(endpoint_image, headers={"Authorization": "Client-ID {}".format(roxbot.imgur_token)})
		if bool(resp["success"]) is True:
			return resp["data"]["link"]
		else:
			resp = await roxbot.http.api_request(endpoint_album,
												 headers={"Authorization": "Client-ID {}".format(roxbot.imgur_token)})
			return resp["data"][0]["link"]


# TODO: Reimplement eroshare, eroshae, and erome support. Also implement better api interaction with imgur now we require api key


async def subreddit_request(subreddit):
	options = [".json?count=1000", "/top/.json?sort=top&t=all&count=1000"]
	choice = random.choice(options)
	subreddit += choice
	url = "https://reddit.com/r/"+subreddit
	r = await roxbot.http.api_request(url)
	try:
		posts = r["data"]
		return posts
	except (KeyError, TypeError):
		return {}


async def parse_url(url):
	if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv", "webm", "mp4", "webp"):
		return url
	if "imgur" in url:
		return await imgur_get(url)
	elif "eroshare" in url or "eroshae" in url or "erome" in url:
		return False
		#return ero_get(url)
	elif "gfycat" in url or "redd.it" in url or "i.reddituploads" in url or "media.tumblr" in url or "streamable" in url:
		return url
	else:
		return False


class Reddit:
	def __init__(self, bot_client):
		self.bot = bot_client
		self.post_cache = {}
		for guild in self.bot.guilds:
			self.post_cache[guild.id] = [("", "")]

	@commands.command()
	@commands.has_permissions(add_reactions=True)
	@commands.bot_has_permissions(add_reactions=True)
	async def subreddit(self, ctx, subreddit):
		"""
		Grabs an image or video (jpg, png, gif, gifv, webm, mp4) from the subreddit inputted.
		Example:
		{command_prefix}subreddit pics
		"""
		subreddit = subreddit.lower()
		links = await subreddit_request(subreddit)
		title = ""
		choice = {}

		if not links or not links.get("after") or links["children"][0]["kind"] == "t5":  # Determine if response is valid
			return await ctx.send("Error ;-; That subreddit probably doesn't exist. Please check your spelling")

		url = ""
		x = 0
		# While loop here to make sure that we check if there is any image posts in the links we have. If so, just take the first one.
		# Choosing a while loop here because, for some reason, the for loop would never exit till the end. Leading to slow times.
		while not url and x <= 20:
			choice = random.choice(links["children"])["data"]
			url = await parse_url(choice["url"])
			if url:
				x_old = int(x)
				# If the url or id are in the cache,  continue the loop. If not, proceed with the post.
				for cache in self.post_cache[ctx.guild.id]:
					if url in cache or choice["id"] in cache:
						x += 1
						break
				if x_old != x:  # Has x been incremented
					url = ""  # Restart search for new post
					if x > 20:
						break
					continue

				title = "**{}** \nby /u/{} from /r/{}\n".format(unescape(choice["title"]), unescape(choice["author"]), subreddit)
				break
			else:
				x += 1

		# Check if post is NSFW, and if it is and this channel doesn't past the NSFW check, then return with the error message.
		if choice["over_18"] and not roxbot.checks.nsfw_predicate(ctx):
			return await ctx.send("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
		if not url:  # If no image posts could be found with the for loop.
			return await ctx.send("I couldn't find any images from that subreddit.")

		# Put the post ID and url in the cache. The url is used in case of two posts having the same link. Handy to avoid crossposts getting through the cache.
		cache_amount = 10
		post = (choice["id"], url)
		self.post_cache[ctx.guild.id].append(post)
		if len(self.post_cache[ctx.guild.id]) >= cache_amount:
			self.post_cache[ctx.guild.id].pop(0)

		if url.split("/")[-2] == "a":
			text = "This is an album, click on the link to see more.\n"
		else:
			text = ""

		if ctx.invoked_with == "subreddit":
			# Only log the command when it is this command being used. Not the inbuilt commands.
			logging = guild_settings.get(ctx.guild).logging
			log_channel = self.bot.get_channel(logging["channel"])
			await roxbot.log(
				ctx.guild,
				log_channel,
				"subreddit",
				User=ctx.author,
				Subreddit=subreddit,
				Returned="<{}>".format(url),
				Channel=ctx.channel,
				Channel_Mention=ctx.channel.mention,
				Time="{:%a %Y/%m/%d %H:%M:%S} UTC".format(ctx.message.created_at)
			)

		# Not using a embed here because we can't use video in rich embeds but they work in embeds now :/
		output = await ctx.send(title + text + url)
		await roxbot.utils.delete_option(self.bot, ctx, output, self.bot.get_emoji(444410658101002261) or "❌")

	@commands.command()
	async def aww(self, ctx):
		"""
		Gives you cute pics from reddit
		"""
		subreddit = "aww"
		return await ctx.invoke(self.subreddit, subreddit=subreddit)

	@commands.command()
	async def feedme(self, ctx):
		"""
		Feeds you with food porn. Uses multiple subreddits.
		Yes, I was very hungry when trying to find the subreddits for this command.
		Subreddits: "foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"
		"""
		subreddits = ["foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"]
		subreddit_choice = random.choice(subreddits)
		return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

	@commands.command()
	async def feedmevegan(self, ctx):
		"""
		Feeds you with vegan food porn. Uses multiple subreddits.
		Yes, I was very hungry when trying to find the subreddits for this command.
		Subreddits: "veganrecipes", "vegangifrecipes", "veganfoodporn"
		"""
		subreddits = ["veganrecipes", "vegangifrecipes", "VeganFoodPorn"]
		subreddit_choice = random.choice(subreddits)
		return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

	@commands.command(aliases=["gssp"])
	async def gss(self, ctx):
		"""
		Gives you the best trans memes ever
		"""
		subreddit = "gaysoundsshitposts"
		return await ctx.invoke(self.subreddit, subreddit=subreddit)

	@commands.command(hidden=True, name="subreddit_dryrun")
	async def _subreddit_test(self, ctx, url):
		return await ctx.send(await parse_url(url))


def setup(bot_client):
	bot_client.add_cog(Reddit(bot_client))
