"""
Writes an email if some rule is satisfied
Written by /u/SmBe19
"""

import praw
import time
import smtplib
import logging
import logging.handlers
import OAuth2Util

# ### USER CONFIGURATION ### #

# The bot's useragent. It should contain a short description of what it does and your username. e.g. "RSS Bot by /u/SmBe19"
USERAGENT = ""

# The time in seconds the bot should sleep until it checks again.
SLEEP = 60

# The number of seconds the age of a post can be off and is still seen as within the timespan (e.g. if 20 ups in 20 minutes are searched, with a buffer of 5 minutes a post with 20 ups which is 25 minutes old will still be considered)
AGE_BUFFER = 10*60

# Hostname of the smtp server to use
SMTP_HOST = "smtp.gmail.com"

# Port of the smtp server to use
SMTP_PORT = 587

# EMail address to use as sender
SMTP_FROM = "me@example.com"

# Username to login on the smtp server
SMTP_USERNAME = "me@example.com"

# Password to login on the smtp server
SMTP_PASSWORD = "123"

# Subject line of the message for the SubNewPost rule. {0} will be replaced by the Subreddit name
SUBJECT_SUBNEWPOST_TEXT = "New Post in {0}"

# Subject line of the message for the UserNewPost rule. {0} will be replaced by the user name
SUBJECT_USERNEWPOST_TEXT = "New Post from {0}"

# Subject line of the message for the UserInSubNewPost rule. {0} will be replaced by the Subreddit name, {1} by the user name
SUBJECT_USERINSUBNEWPOST_TEXT = "New Post in {0} from {1}"

# Subject line of the message for the SubNewComment rule. {0} will be replaced by the Subreddit name
SUBJECT_SUBNEWCOMMENT_TEXT = "New Comment in {0}"

# Subject line of the message for the UserNewComment rule. {0} will be replaced by the user name
SUBJECT_USERNEWCOMMENT_TEXT = "New Comment from {0}"

# Subject line of the message for the UserInSubNewComment rule. {0} will be replaced by the Subreddit name, {1} by the user name
SUBJECT_USERINSUBNEWCOMMENT_TEXT = "New Comment in {0} from {1}"

# Subject line of the message for the VotesInTime rule. {0} will be replaced by the Subreddit name
SUBJECT_VOTESINTIME_TEXT = "New Post in {0} satisfying rule"

# Body of the message. {0} will be replaced by the permalink, {1} by the title, {2} will be replaced by the URL, {3} by selftext
BODY_TEXT = "Comments: {0}\n\nTitle: {1}\n\nURL: {2}\n\nSelftext: {3}"

# If true, no messages will be sent. Used to initialize a new Subreddit / User so you don't get a message for every post already existing.
BUILD_DONE_LIST = False
# ### END USER CONFIGURATION ### #

# ### BOT CONFIGURATION ### #
DONE_CONFIGFILE = "done.txt"
RULES_CONFIGFILE = "rules.txt"
# ### END BOT CONFIGURATION ### #

# ### LOGGING CONFIGURATION ### #
LOG_LEVEL = logging.INFO
LOG_FILENAME = "bot.log"
LOG_FILE_BACKUPCOUNT = 5
LOG_FILE_MAXSIZE = 1024 * 256
# ### END LOGGING CONFIGURATION ### #

# ### EXTERNAL CONFIG FILE ### #
try:
	# A file containing data for global constants.
	import bot
	for k in dir(bot):
		if k.upper() in globals():
			globals()[k.upper()] = getattr(bot, k)
except ImportError:
	pass
# ### END EXTERNAL CONFIG FILE ### #

# ### LOGGING SETUP ### #
log = logging.getLogger("bot")
log.setLevel(LOG_LEVEL)
log_formatter = logging.Formatter('%(levelname)s: %(message)s')
log_formatter_file = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
log_stderrHandler = logging.StreamHandler()
log_stderrHandler.setFormatter(log_formatter)
log.addHandler(log_stderrHandler)
if LOG_FILENAME is not None:
	log_fileHandler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=LOG_FILE_MAXSIZE, backupCount=LOG_FILE_BACKUPCOUNT)
	log_fileHandler.setFormatter(log_formatter_file)
	log.addHandler(log_fileHandler)
# ### END LOGGING SETUP ### #

def read_config_rules():
	rules = []
	try:
		with open(RULES_CONFIGFILE, "r") as f:
			for line in f:
				if line.startswith("#"):
					continue
				rules.append([w.strip().lower() for w in line.split("\t")])
	except OSError:
		log.error("%s not found.", RULES_CONFIGFILE)
	return rules


def read_config_done():
	done = set()
	try:
		with open(DONE_CONFIGFILE, "r") as f:
			for line in f:
				if line.strip():
					done.add(line.strip())
	except OSError:
		log.info("%s not found.", DONE_CONFIGFILE)
	return done

def write_config_done(done):
	with open(DONE_CONFIGFILE, "w") as f:
		for d in done:
			if d:
				f.write(d + "\n")

def send_email(recipient, subject, message):
	smtp = smtplib.SMTP(host=SMTP_HOST, port=SMTP_PORT)
	smtp.starttls()
	if SMTP_USERNAME and SMTP_PASSWORD:
		try:
			smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
		except smtplib.SMTPAuthenticationError:
			log.error("Wrong email password")

	try:
		smtp.sendmail(SMTP_FROM, recipient, "Subject: {0}\n\n{1}".format(subject, message))
	except Exception as e:
		log.error("Error while sending mail, %s", e, exc_info=True)
	smtp.quit()

def send_message(r, recipient, subject, message):
	if recipient.startswith("/u/"):
		if not r.is_oauth_session():
			log.warning("Recipient is Reddit user, but you are not logged in. Rule ignored.")
			return
		r.send_message(r.get_redditor(recipient[3:]), subject, message)
	else:
		send_email(recipient, subject, message)

# main procedure
def run_bot():
	r = praw.Reddit(USERAGENT)
	o = OAuth2Util.OAuth2Util(r)
	o.refresh()

	log.info("Start bot")

	done = read_config_done()

	while True:
		try:
			o.refresh()
			rules = read_config_rules()
			old_done = done[:]
			for rule in rules:
				log.info("process rule %s", rule[1])
				if rule[0].startswith("/u/") and not r.is_oauth_session():
					log.warning("Recipient is Reddit user, but you are not logged in. Rule ignored.")
					continue
				if rule[1] == "votesintime":
					sub = r.get_subreddit(rule[2])
					age = int(rule[3])
					ups = int(rule[4])

					for post in sub.get_new(limit=100):
						diff = abs((time.time() - post.created_utc) - age)
						if diff <= AGE_BUFFER and post.score >= ups:
							if post.name in old_done:
								continue
							if not BUILD_DONE_LIST:
								send_message(r, rule[0], SUBJECT_VOTESINTIME_TEXT.format(sub.display_name), BODY_TEXT.format(post.permalink, post.title, post.url, post.selftext))
							done.add(post.name)
							log.info("found new post for rule %s", rule[1])

				elif rule[1] == "usernewpost":
					redditor = r.get_redditor(rule[2])
					for post in redditor.get_submitted(limit=100):
						if post.name in old_done:
							continue
						if not BUILD_DONE_LIST:
							send_message(r, rule[0], SUBJECT_USERNEWPOST_TEXT.format(redditor.name), BODY_TEXT.format(post.permalink, post.title, post.url, post.selftext))
						done.add(post.name)
						log.info("found new post for rule %s", rule[1])

				elif rule[1] == "subnewpost":
					sub = r.get_subreddit(rule[2])
					for post in sub.get_new(limit=100):
						if post.name in old_done:
							continue
						if not BUILD_DONE_LIST:
							send_message(r, rule[0], SUBJECT_SUBNEWPOST_TEXT.format(sub.display_name), BODY_TEXT.format(post.permalink, post.title, post.url, post.selftext))
						done.add(post.name)
						log.info("found new post for rule %s", rule[1])

				elif rule[1] == "userinsubnewpost":
					redditor = r.get_redditor(rule[2])
					for post in redditor.get_submitted(limit=100):
						if post.subreddit.display_name.lower() != rule[3] or post.name in old_done:
							continue
						if not BUILD_DONE_LIST:
							send_message(r, rule[0], SUBJECT_USERINSUBNEWPOST_TEXT.format(post.subreddit.display_name, redditor.name), BODY_TEXT.format(post.permalink, post.title, post.url, post.selftext))
						done.add(post.name)
						log.info("found new post for rule %s", rule[1])

				elif rule[1] == "usernewcomment":
					redditor = r.get_redditor(rule[2])
					for comment in redditor.get_comments(limit=100):
						if comment.name in old_done:
							continue
						if not BUILD_DONE_LIST:
							send_message(r, rule[0], SUBJECT_USERNEWCOMMENT_TEXT.format(redditor.name), BODY_TEXT.format(comment.permalink, comment.link_title, comment.link_url, comment.body))
						done.add(comment.name)
						log.info("found new comment for rule %s", rule[1])

				elif rule[1] == "subnewcomment":
					sub = r.get_subreddit(rule[2])
					for comment in sub.get_comments(limit=100):
						if comment.name in old_done:
							continue
						if not BUILD_DONE_LIST:
							send_message(r, rule[0], SUBJECT_SUBNEWCOMMENT_TEXT.format(sub.display_name), BODY_TEXT.format(comment.permalink, comment.link_title, comment.link_url, comment.body))
						done.add(comment.name)
						log.info("found new comment for rule %s", rule[1])

				elif rule[1] == "userinsubnewcomment":
					redditor = r.get_redditor(rule[2])
					for comment in redditor.get_comments(limit=100):
						if comment.subreddit.display_name.lower() != rule[3] or comment.name in old_done:
							continue
						if not BUILD_DONE_LIST:
							send_message(r, rule[0], SUBJECT_USERINSUBNEWCOMMENT_TEXT.format(comment.subreddit.display_name, redditor.name), BODY_TEXT.format(comment.permalink, comment.link_title, comment.link_url, comment.body))
						done.add(comment.name)
						log.info("found new comment for rule %s", rule[1])

				write_config_done(done)

		# Allows the bot to exit on ^C, all other exceptions are ignored
		except KeyboardInterrupt:
			break
		except Exception as e:
			log.error("Exception %s", e, exc_info=True)

		write_config_done(done)
		log.info("sleep for %s s", SLEEP)
		time.sleep(SLEEP)

	write_config_done(done)


if __name__ == "__main__":
	if not USERAGENT:
		log.error("missing useragent")
	else:
		run_bot()