SHARE
TWEET

TotesMessenger!

justcool939 Mar 30th, 2015 (edited) 1,056 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import logging, os, praw, re, time, traceback, sys, urllib2, requests.exceptions;
  2.  
  3. linked = [];
  4. linkedsrc = [];
  5. skipped = [];
  6. skippedsrc = [];
  7. linkedcount = 0;
  8. errorcount = 0;
  9.  
  10. TESTING = False;
  11. ARCHIVE_TIME = 15778463; # currently 6 months (in seconds)
  12. CRASH_TIMER = 60;
  13. CJ_HEADER = u"""This dank meme has been linked to from another place on le reddit.""";
  14. HEADER = u"""This thread has been linked to from another place on reddit.""";
  15. FOOTER = u"""[](#footer)*^If ^you ^follow ^any ^of ^the ^above ^links, ^respect ^the ^rules ^of ^reddit ^and ^don't ^vote. ^\([Info](/r/TotesMessenger/wiki/) ^/ ^[Contact](/message/compose/?to=\/r\/TotesMessenger))* [](#bot)""";
  16.  
  17. user = os.environ['REDDIT_USER'];
  18. blacklist = ["anime", "asianamerican", "askhistorians", "askscience", "aww", "benfrick", "bmw", "chicagosuburbs",
  19.              "cosplay", "cumberbitches", "d3gf", "deer", "depression", "depthhub", "drinkingdollars",
  20.              "forwardsfromgrandma", "futurology", "geckos", "giraffes", "graphical_design", "grindsmygears",
  21.              "indianfetish", "misc", "mixedbreeds", "news", "newtotf2", "omaha", "petstacking", "pigs",
  22.              "politicaldiscussion", "programmingcirclejerk", "raerthdev", "rants", "salvia", "science",
  23.              "seiko", "shoplifting", "sketches", "sociopath", "suicidewatch", "talesfromtechsupport", "unitedkingdom"];
  24. # Do not edit
  25.  
  26. srcblacklist = ["depression", "lifeafternarcissists", "managedbynarcissists", "moderationlog", "raisedbynarcissists",
  27.                 "rbnathome", "rbnbookclub", "rbnchildcare", "rbnfavors", "rbngames", "rbnlifeskills", "rbnmovienight",
  28.                 "rbnrelationships", "rbnspouses", "suicidewatch", "switcharoo", "switcheroo", "trolledbynarcissists",
  29.                 "unremovable", "politic", "mlplite", "risingthreads", "uncensorship", "leagueofriot", "benlargefanclub",
  30.                 "fitnesscirclejerk", "taiwancirclejerk", "requestedtweaks", "jaxbrew", "floridabrew", "aggregat0r",
  31.                 "gamecollectingjerk", "technews2015"];
  32.  
  33. banned = ["reddit.com", "minecraft", "adviceanimals", "askreddit", "worldnews", "femradebates", "pcmasterrace",
  34.           "purplepilldebate", "slrep", "funny", "theredpill", "personalfinance", "india", "lifehacks", "kotakuinaction",
  35.           "askmen", "smashbros", "android", "neutralpolitics", "dota2", "wet_shavers", "dogecoin", "askphilosophy",
  36.           "suits", "japanlife", "photography", "hiphopheads", "apple", "lifeprotips", "nba", "dbz", "gender_critical",
  37.           "movies"];
  38.  
  39. blockedusers = ["amprobablypooping", "evilrising", "frontpagewatch", "frontpagewatchmirror", "moon-done", "politicbot",
  40.                 "rising_threads_bot", "removal_rover", "know_your_shit", "drugtaker", "nedsc"];
  41.  
  42. # metabots = [user, "totesmessenger", "totes_meta_bot", "meta_bot", "meta_bot2", "originallinkbot"];
  43.  
  44. # Ban list:
  45. # drugtaker - Meta bot NSFW marking evasion
  46. # NedSc     - By request
  47.  
  48. nsfwreddits = ["srsshillwatch", "srsshillswatch", "srshillswatch", "srshillwatch", "gonewild"];
  49.  
  50. test_reddits = ["justcool393", "tmtest", "totesmessenger"];
  51.  
  52.  
  53.  
  54. def main():
  55.     global linked;
  56.     global linkedsrc;
  57.     global skipped;
  58.     global skippedsrc;
  59.     global linkedcount;
  60.     global errorcount;
  61.  
  62.     create_files();
  63.  
  64.     linked = load_list("linked.lst");
  65.     linkedsrc = load_list("linkedsrc.lst");
  66.     skipped = load_list("skipped.lst");
  67.     skippedsrc = load_list("skippedsrc.lst");
  68.  
  69.     r = praw.Reddit("Links to reddit posts from other places in reddit", domain="api.reddit.com", log_requests=0);
  70.     r.login(user, os.environ['REDDIT_PASS']);
  71.     logging.info("Logged in to reddit...");
  72.  
  73.     add_linked(r);
  74.     logging.info("Linked: " + str(len(linked)) + ", source: " + str(len(linkedsrc)));
  75.  
  76.     check_at = 3600;
  77.     # save_at = 60;
  78.     last_logged = 0;
  79.     # last_saved = 0;
  80.     times_zero = 1;
  81.  
  82.     link_subs(r, 100, 120); # Check the last 100 posts on startup
  83.     while True:
  84.         '''
  85.        if time.time() >= (last_saved + save_at):
  86.            last_saved = time.time();
  87.            save_list("linked.lst", linked);
  88.            save_list("linkedsrc.lst", linkedsrc);
  89.            save_list("skipped.lst", skipped);
  90.            save_list("skippedsrc.lst", skippedsrc);
  91.        ''' # Saving is disabled due to our host works.
  92.  
  93.         if time.time() >= (last_logged + check_at):
  94.  
  95.             last_logged = time.time();
  96.             if linkedcount == 0:
  97.                 times_zero += 1;
  98.             else:
  99.                 logging.info("Last " + str((check_at * times_zero) / 60 / 60) + " hr(s): Linked " + str(linkedcount)
  100.                              + ", " + str(errorcount) + " failed.");
  101.                 # logging.info("Linked " + str(count) + " in the last " + str((check_at * times_zero) / 60 / 60) + " hour(s)");
  102.                 linkedcount = 0;
  103.                 errorcount = 0;
  104.                 times_zero = 1;
  105.         link_subs(r, 25, 60);
  106.  
  107. def add_linked(r):
  108.     for c in r.user.get_comments(sort='new', limit=None):
  109.         pid = c.parent_id;
  110.         if pid is None:
  111.             continue;
  112.         if pid not in linked:
  113.             linked.append(pid);
  114.         # Add linkedsrc one in to one method.
  115.         posts = re.findall("http://np.reddit.com/r/.{1,20}/comments/.{1,8}/", c.body);
  116.         for p in posts:
  117.             linkedsrc.append(re.sub("http://np.reddit.com/r/.{1,20}/comments/", "", p)[:-1]);
  118.  
  119. def create_files():
  120.     f = open("linked.lst", "a");
  121.     f.close();
  122.     f = open("linkedsrc.lst", "a");
  123.     f.close();
  124.     f = open("skipped.lst", "a");
  125.     f.close();
  126.     f = open("skippedsrc.lst", "a");
  127.     f.close();
  128.  
  129. def link_subs(r, count, delay):
  130.     global linkedcount;
  131.     global errorcount;
  132.     for submission in r.get_domain_listing('reddit.com', sort='new', limit=count):
  133.  
  134.         if TESTING and submission.subreddit.display_name.lower() not in test_reddits:
  135.             continue;
  136.  
  137.         try:
  138.             if link_submission(r, submission):
  139.                 linkedcount += 1;
  140.                 time.sleep(3);
  141.             else:
  142.                 errorcount += 1;
  143.         except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as ex:
  144.             logging.error(str(ex));
  145.             errorcount += 1;
  146.             time.sleep(10);
  147.  
  148.     time.sleep(delay);
  149.  
  150.  
  151. def link_submission(r, submission):
  152.     url = re.sub("(\#|\?).{1,}", "", submission.url);
  153.     if not is_comment(url):
  154.         return False;
  155.     linkedp = None;
  156.     try:
  157.         linkedp = get_object(r, url);
  158.     except praw.errors.ClientException:
  159.         logging.error("Link is not a reddit post (id: " + submission.id + ")");
  160.         logging.error(exi());
  161.         return False;
  162.     except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as ex:
  163.         logging.error(str(ex));
  164.         time.sleep(5);
  165.         return False;
  166.     except Exception:
  167.         logging.error("Could not get comment!");
  168.         logging.error(exi());
  169.  
  170.     if linkedp is None:
  171.         return False;
  172.  
  173.     lid = linkedp.id;
  174.     sid = submission.id;
  175.  
  176.     # Skip conditions: Already deleted, undelete/scraper/mod log bots, blacklisted, banned/archived,
  177.     # archived, in source blacklist
  178.  
  179.     if linkedp.subreddit is None or linkedp.author is None:
  180.         skipped.append(lid);
  181.         return False;
  182.  
  183.     srlower = linkedp.subreddit.display_name.lower();
  184.  
  185.     if srlower in blacklist or srlower in banned or linkedp.created < (time.time() - ARCHIVE_TIME):
  186.         skipped.append(lid);
  187.         return False;
  188.  
  189.     if linkedp.subreddit.user_is_banned:
  190.         skipped.append(lid);
  191.         return False;
  192.  
  193.     if submission.subreddit.display_name.lower() in srcblacklist or submission.author is None:
  194.         skippedsrc.append(sid);
  195.         return False;
  196.  
  197.     if submission.author.name.lower() in blockedusers:
  198.         skippedsrc.append(sid);
  199.         return False;
  200.  
  201.     if lid in skipped or sid in skippedsrc:
  202.         return False;
  203.  
  204.     if sid in linkedsrc:
  205.         return False;
  206.  
  207.  
  208.     if lid in linked or check_commmented(linkedp) or get_bot_comment(linkedp) is not None:
  209.         success = edit_post(get_bot_comment(linkedp), submission);
  210.         linkedsrc.append(sid);
  211.         if lid not in linked:
  212.             linked.append(lid);
  213.         return success;
  214.  
  215.     cj = srlower == "circlejerk"; # check if our subreddit is /r/circlejerk so we can user our specialized msg for it
  216.  
  217.     if isinstance(linkedp, praw.objects.Comment):
  218.         comment(linkedp, submission, cj);
  219.     elif isinstance(linkedp, praw.objects.Submission):
  220.         post(linkedp, submission, cj);
  221.     else:
  222.         logging.error("Not a Comment or Submission! (ID: " + lid + ")");
  223.         return False;
  224.  
  225.     linked.append(lid);
  226.     linkedsrc.append(sid);
  227.     return True;
  228.  
  229.  
  230. def edit_post(totessubmission, original):
  231.     if totessubmission is None:
  232.         return False;
  233.     text = re.sub("\[\]\(#footer\).{1,}", "", totessubmission.body); # sub. invisible link for easier footer changes
  234.     text = re.sub("\*\^If.{1,}", "", text);
  235.     text = re.sub("\^Please.{1,}", "", text); # substitute old footer as well
  236.     text = re.sub("Do not vote.{1,}", "", text); # substitute original footer as well
  237.     text = text + format_link(original) + u"""
  238.  
  239.  
  240. """ + FOOTER;
  241.     totessubmission.edit(text);
  242.     return True;
  243.  
  244.  
  245. def get_comment(r, s):
  246.     return get_linked(r, s).comments[0];
  247.  
  248.  
  249. def get_linked(r, link):
  250.     return r.get_submission(link);
  251.  
  252.  
  253. def check_commmented(c):
  254.     if isinstance(c, praw.objects.Comment):
  255.         comments = c.replies;
  256.     elif isinstance(c, praw.objects.Submission):
  257.         c.replace_more_comments(limit=None, threshold=0);
  258.         comments = praw.helpers.flatten_tree(c.comments);
  259.  
  260.     for co in comments:
  261.         if co.author is None:
  262.             continue;
  263.         if co.author.name.lower() == user.lower():
  264.             return True;
  265.     return False;
  266.  
  267.  
  268. def get_bot_comment(s):
  269.     if isinstance(s, praw.objects.Comment):
  270.         for c in s.replies:
  271.             if c.author is None:
  272.                 continue;
  273.             if c.author.name.lower() == user.lower():
  274.                 return c;
  275.     else:
  276.         s.replace_more_comments(limit=None, threshold=0);
  277.         flat_comments = praw.helpers.flatten_tree(s.comments);
  278.         for c in flat_comments:
  279.             if c.author is None:
  280.                 continue;
  281.             if c.author.name.lower() == user.lower():
  282.                 return c;
  283.     return None;
  284.  
  285.  
  286. def format_comment(original, isrcirclejerk):
  287.     if isrcirclejerk:
  288.         cmt = CJ_HEADER;
  289.     else:
  290.         cmt = HEADER;
  291.     cmt = cmt + u"""
  292.  
  293. {link}
  294.  
  295. """ + FOOTER;
  296.  
  297.     return cmt.format(link=format_link(original));
  298.  
  299.  
  300. def post(s, original, isrcirclejerk):
  301.     try:
  302.         s.add_comment(format_comment(original, isrcirclejerk));
  303.     except praw.errors.RateLimitExceeded:
  304.         logging.debug("Can't comment (comment karma is too low)");
  305.     except praw.errors.APIException as e:
  306.         logging.warning(str(e));
  307.     except Exception:
  308.         logging.error("Error adding comment (SID: " + str(s.id) + ")");
  309.         logging.error(exi());
  310.  
  311.  
  312. def comment(c, original, isrcirclejerk):
  313.     try:
  314.         c.reply(format_comment(original, isrcirclejerk));
  315.     except praw.errors.RateLimitExceeded:
  316.         logging.debug("Can't comment (comment karma is too low)");
  317.     except praw.errors.APIException as e:
  318.         logging.warning(str(e));
  319.     except Exception as e:
  320.         logging.error("Error adding comment (CID: " + str(c.id) + ")");
  321.         logging.error(str(e));
  322.  
  323.  
  324. def format_link(post):
  325.     srurl = post.subreddit.url;
  326.     nsfw = post.subreddit.name.lower() in nsfwreddits or post.subreddit.over18 or post.over_18;
  327.     text = u"- [" + srurl[:-1] + "] ";
  328.     if nsfw:
  329.         text = text + u"[NSFW] ";
  330.     return text + u"[" + post.title + "](" + np(post.permalink) + ")\n";
  331.  
  332. def changesubdomain(link, sub):
  333.     l = re.sub(r"http[s]?://[a-z]{0,3}\.?[a-z]{0,2}\.?reddit\.com", "", link);
  334.     return "http://" + sub + ".reddit.com" + l;
  335.  
  336. def unnp(link):
  337.     return changesubdomain(link, "www");
  338.  
  339.  
  340. def np(link):
  341.     return changesubdomain(link, "np");
  342.  
  343.  
  344. def get_cid(url):
  345.     l = re.sub(r"http[s]?://[a-z]{0,3}\.?reddit\.com/r/.{1,20}/comments/.{6,8}/.*/", "", url);
  346.     l = re.sub(r"\?.*", "", l);
  347.     l = re.sub(r"\..*", "", l);
  348.     return "t1_" + l;
  349.  
  350.  
  351. def get_object(r, url):
  352.     url = unnp(url);
  353.     obj = praw.objects.Submission.from_url(r, url);
  354.     a = re.compile("http[s]?://[a-z]{0,3}\.?reddit\.com/r/.{1,20}/comments/.{6,8}/.*/.{6,8}");
  355.  
  356.     if a.match(url):
  357.         o = r.get_info(thing_id=get_cid(url));
  358.         if o is None:
  359.             logging.error("Not a comment! (URL: " + url + ")");
  360.  
  361.         return o;
  362.     else:
  363.         return obj;
  364.  
  365.  
  366. def is_comment(link):
  367.     a = re.compile("http[s]?://[a-z]{0,3}\.?[a-z]{0,2}\.?reddit\.com/r/.{1,20}/comments/.*");
  368.     return a.match(link);
  369.  
  370.  
  371. def load_list(file):
  372.     f = open(file, "r");
  373.     data = f.read();
  374.     f.close();
  375.     return data.split();
  376.  
  377. def save_list(file, list):
  378.     f = open(file, "wb");
  379.     str = "";
  380.     for s in list:
  381.         str = str + s + " ";
  382.     f.write(str);
  383.     f.close();
  384.  
  385.  
  386. def log_crash():
  387.     logging.error("Details: ");
  388.     logging.error(exi());
  389.     logging.error("Unhandled exception. Restarting in " + str(CRASH_TIMER) + " seconds...");
  390.     time.sleep(CRASH_TIMER);
  391.     sys.exit(1);
  392.  
  393.  
  394. def exi():
  395.     return traceback.format_exc();
  396.  
  397.  
  398. def setup_logging():
  399.     root = logging.getLogger();
  400.     root.setLevel(logging.INFO);
  401.     logging.getLogger("requests").setLevel(logging.WARNING);
  402.     ch = logging.StreamHandler(sys.stdout);
  403.     ch.setLevel(logging.INFO);
  404.  
  405.     root.addHandler(ch);
  406.  
  407. try:
  408.     setup_logging();
  409.     main();
  410. except (AttributeError, NameError, SyntaxError, TypeError, UnboundLocalError) as e:
  411.     logging.error("Crash due to syntactical error!");
  412.     logging.error(exi());
  413.     time.sleep(86400);  # Sleep for 1 day so we don't restart.
  414. except Exception:
  415.     log_crash();
RAW Paste Data
Top