Advertisement
Guest User

Untitled

a guest
Jul 12th, 2017
411
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.54 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import os
  4. import sys
  5. import json
  6. import logging
  7. import hashlib
  8. import requests
  9. import time
  10. import getpass
  11. import csv
  12. from HTMLParser import HTMLParser
  13. import codecs
  14. import re
  15.  
  16.  
  17. def remove_windows_characters(s):
  18.     chars = {
  19.         '\xc2\x82' : ',',        # High code comma
  20.         '\xc2\x84' : ',,',       # High code double comma
  21.         '\xc2\x85' : '...',      # Tripple dot
  22.         '\xc2\x88' : '^',        # High carat
  23.         '\xc2\x91' : '\x27',     # Forward single quote
  24.         '\xc2\x92' : '\x27',     # Reverse single quote
  25.         '\xc2\x93' : '\x22',     # Forward double quote
  26.         '\xc2\x94' : '\x22',     # Reverse double quote
  27.         '\xc2\x95' : ' ',
  28.         '\xc2\x96' : '-',        # High hyphen
  29.         '\xc2\x97' : '--',       # Double hyphen
  30.         '\xc2\x99' : ' ',
  31.         '\xc2\xa0' : ' ',
  32.         '\xc2\xa6' : '|',        # Split vertical bar
  33.         '\xc2\xab' : '<<',       # Double less than
  34.         '\xc2\xbb' : '>>',       # Double greater than
  35.         '\xc2\xbc' : '1/4',      # one quarter
  36.         '\xc2\xbd' : '1/2',      # one half
  37.         '\xc2\xbe' : '3/4',      # three quarters
  38.         '\xca\xbf' : '\x27',     # c-single quote
  39.         '\xcc\xa8' : '',         # modifier - under curve
  40.         '\xcc\xb1' : ''          # modifier - under line
  41.     }
  42.     def replace_chars(match):
  43.         char = match.group(0)
  44.         return chars[char]
  45.     return re.sub('(' + '|'.join(chars.keys()) + ')', replace_chars, s)
  46.  
  47.  
  48. def restore_windows_1252_characters(s):
  49.     def to_windows_1252(match):
  50.         try:
  51.             return bytes([ord(match.group(0))]).decode('windows-1252')
  52.         except UnicodeDecodeError:
  53.             return ''
  54.     return re.sub(r'[\u0080-\u0099]', to_windows_1252, s)
  55.  
  56.  
  57. class AutoAdmitParser(HTMLParser):
  58.     def __init__(self, *args, **kwargs):
  59.         self.payload = {}
  60.         self._in_textarea = False
  61.         HTMLParser.__init__(self, *args, **kwargs)
  62.  
  63.     def handle_starttag(self, tag, attrs):
  64.         if tag == "input":
  65.             _attrs = dict(attrs)
  66.             if _attrs.get("type", None) != "submit" and "name" in _attrs:
  67.                 self.payload[_attrs["name"]] = _attrs.get("value", None)
  68.         elif tag == "textarea":
  69.             self._in_textarea = True
  70.  
  71.     def handle_endtag(self, tag):
  72.         if tag == "textarea":
  73.             self._in_textarea = False
  74.  
  75.     def handle_data(self, data):
  76.         if self._in_textarea is True:
  77.             if self.payload.get("message", None) is not None:
  78.                 self.payload["message"] += data
  79.             else:
  80.                 self.payload["message"] = data
  81.  
  82.  
  83. class Blanker(object):
  84.  
  85.     USER_AGENT = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 " \
  86.                  "like Mac OS X; nb-no) AppleWebKit/533.17.9 " \
  87.                  "(KHTML, like Gecko) Version/5.0.2 Mobile/8C148a " \
  88.                  "Safari/6533.18.5"
  89.     LOG_FILE = "blanker.log"
  90.     MIN_WAIT_DURATION = 2.5
  91.     HTTP_REFERER_TEMPLATE = "http://xoxohth.com/thread.php?" \
  92.                             "thread_id={}&forum_id=2"
  93.     HTTP_GET_TEMPLATE = "http://xoxohth.com/post.php?" \
  94.                         "message_id={}&" \
  95.                         "thread_id={}&forum_id=2"
  96.     HTTP_POST_TEMPLATE = "http://xoxohth.com/post.php"
  97.     CSV_FILE = "old_posts.csv"
  98.     CSV_FIELD_NAMES = ["thread_id", "message_id", "poster_email",
  99.                        "message_subject", "message"]
  100.                        
  101.    
  102.     def __init__(self, logger, posts, **kwargs):
  103.         self._username = kwargs.get("username", None)
  104.         self._password = kwargs.get("password", None)
  105.         self.posts = posts
  106.         self.logger = logger
  107.         self.session = requests.Session()
  108.         self._processed_messages = []
  109.  
  110.     def prepare_csv(self):
  111.         """Prepares the csv file for storing old posts by reading in the
  112.       already-processed message_ids"""
  113.         if os.path.exists(self.CSV_FILE):
  114.             with open(self.CSV_FILE, 'rb') as csvf:
  115.                 reader = csv.DictReader(csvf)
  116.                 for row in reader:
  117.                     self._processed_messages.append(int(row["message_id"]))
  118.                 print self._processed_messages
  119.         else:
  120.             with open(self.CSV_FILE, 'wb') as csvf:
  121.                 writer = csv.DictWriter(csvf, fieldnames=self.CSV_FIELD_NAMES)
  122.                 writer.writeheader()
  123.  
  124.     def get_wait(self):
  125.         return self.MIN_WAIT_DURATION + \
  126.             (int(hashlib.sha256(os.urandom(2**15)).hexdigest(), 16) %
  127.              10**6)/1e6
  128.  
  129.     def login(self, **kwargs):
  130.         auth_url = "http://xoxohth.com/login.php"
  131.         form_data = {
  132.             "ref_page": "main.php?forum_id=2",
  133.             "username": self._username,
  134.             "password": self._password,
  135.             "Submit": "Sign In"
  136.         }
  137.         self.session.headers.update({
  138.             "User-Agent": self.USER_AGENT,
  139.             "Referer": kwargs.get(
  140.                 "referer",
  141.                 "http://xoxohth.com/main.php?forum_id=2&pft=1")
  142.         })
  143.         self.logger.info("Attempting to log in as user '{}'...".format(
  144.             self._username))
  145.         r = self.session.post(auth_url, data=form_data)
  146.         if r.text.find("Invalid login. Register if you haven't!") != -1:
  147.             self.logger.error("Unable to sign in.")
  148.             return False
  149.         self.logger.info("Successfully logged in as user '{}'.".format(
  150.             self._username))
  151.         return True
  152.  
  153.     def get_original(self, thread_id, message_id):
  154.         while True:
  155.             # Gets the original post for storage purposes
  156.             self.logger.info(
  157.                 "Getting the original message data for "
  158.                 "message_id={} in thread_id={}...".format(
  159.                     message_id, thread_id))
  160.  
  161.             self.session.headers.update({
  162.                 "User-Agent": self.USER_AGENT,
  163.                 "Referer": self.HTTP_REFERER_TEMPLATE.format(thread_id)
  164.             })
  165.             r = self.session.get(
  166.                 self.HTTP_GET_TEMPLATE.format(message_id, thread_id))
  167.             if r.url.find("login.php") != -1:
  168.                 self.logger.warning("Logged out")
  169.                 failed_attempts = 0
  170.                 time.sleep(self.get_wait())
  171.                 while not self.login():
  172.                     failed_attempts += 1
  173.                     if failed_attempts == 5:
  174.                         self.logger.critical("Could not log in. Stopping.")
  175.                         return False
  176.                     time.sleep(self.get_wait())
  177.             elif r.text == "Nope!":
  178.                 self.logger.error(
  179.                     "You aren't the author of message_id={} in "
  180.                     "thread_id={}!".format(message_id, thread_id))
  181.             else:
  182.                 parser = AutoAdmitParser()
  183.                 parser.feed(r.text)
  184.                 return parser.payload
  185.        
  186.  
  187.     def blank(self, payload, thread_id, message_id):
  188.         while True:
  189.             data = {
  190.                 "action": payload.get("action", "post"),
  191.                 "forum_id": payload.get("forum_id", 2),
  192.                 "thread": payload.get("thread", None),
  193.                 "threadclass": payload.get("threadclass", None),
  194.                 "subj": "",
  195.                 "gmp": payload.get("gmp", None),
  196.                 "sc": payload.get("sc", None),
  197.                 "thread_id": payload.get("thread_id", thread_id),
  198.                 "message_id": payload.get("message_id", message_id),
  199.                 "parent_thread_id": payload.get("parent_thread_id", None),
  200.                 "txtAuthor": payload.get("txtAuthor", None),
  201.                 "poster_email": "",
  202.                 "message_subject": "",
  203.                 "message": "",
  204.                 "taHTML_Code": payload.get("taHTML_Code", None),
  205.                 "cbEmbeddedImages": payload.get("cbEmbeddedImages", None)
  206.             }
  207.             self.session.headers.update({
  208.                 "Referer": self.HTTP_GET_TEMPLATE.format(message_id, thread_id)
  209.             })
  210.             r = self.session.post(self.HTTP_POST_TEMPLATE, data=data)
  211.             if r.url.find("login.php") != -1:
  212.                 self.logger.warning("Logged out")
  213.                 failed_attempts = 0
  214.                 while not self.login():
  215.                     failed_attempts += 1
  216.                     if failed_attempts == 5:
  217.                         self.logger.critical("Could not log in. Stopping.")
  218.                         return False
  219.                     time.sleep(self.get_wait())
  220.             else:
  221.                 self.logger.info(
  222.                     "Message_id={} in thread_id={} was successfully "
  223.                     "blanked.".format(message_id, thread_id))
  224.                 return True
  225.  
  226.     def run(self):
  227.         self.prepare_csv()
  228.         for post in self.posts:
  229.             message_id = post["message_id"]
  230.             thread_id = post["thread_id"]
  231.             if int(message_id) not in self._processed_messages:
  232.                 payload = self.get_original(thread_id, message_id)
  233.                 if payload:
  234.                     self.logger.info(
  235.                         "Successfully retrieved the original message.")
  236.                     with open(self.CSV_FILE, 'ab') as csvf:
  237.                         writer = csv.DictWriter(csvf, self.CSV_FIELD_NAMES)
  238.                         row = dict((k, (remove_windows_characters(v).encode(
  239.                             'utf8', 'ignore')
  240.                                         if isinstance(v, unicode)
  241.                                         else v))
  242.                                    for k,v in payload.items()
  243.                                    if k in self.CSV_FIELD_NAMES)
  244.                         writer.writerow(row)
  245.                         self.logger.info("New CSV row successfully written")
  246.                     self.logger.info(
  247.                         "Attempting to blank message_id={} in "
  248.                         "thread_id={}...".format(message_id, thread_id))
  249.                     time.sleep(self.get_wait())
  250.                     self.blank(payload, thread_id, message_id)
  251.                 time.sleep(self.get_wait())
  252.             else:
  253.                 self.logger.warning(
  254.                     "The message with message_id={} and "
  255.                     "thread_id={} has already been blanked.".format(
  256.                         message_id, thread_id))
  257.  
  258. def usage():
  259.     sys.stderr.write(
  260.         "Post Blanker - Blanks all of your posts on xoxohth.com "
  261.         "(just enter your password when prompted).\n\n"
  262.         "Usage:\n"
  263.         "./blanker.py [path to the json file I sent] [login name]\n")
  264.     sys.stderr.flush()
  265.     exit(1)
  266.  
  267. def main():
  268.     log_formatter = logging.Formatter(
  269.         "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
  270.     logger = logging.getLogger('blanker')
  271.     logger.setLevel(logging.DEBUG)
  272.     file_handler = logging.FileHandler(Blanker.LOG_FILE)
  273.     file_handler.setFormatter(log_formatter)
  274.     file_handler.setLevel(logging.DEBUG)
  275.     logger.addHandler(file_handler)
  276.     console_handler = logging.StreamHandler()
  277.     console_handler.setFormatter(log_formatter)
  278.     console_handler.setLevel(logging.DEBUG)
  279.     logger.addHandler(console_handler)
  280.     if len(sys.argv) != 3:
  281.         usage()
  282.         exit(1)
  283.     with open(sys.argv[1], 'r') as f:
  284.         posts = json.load(f)
  285.     blanker = Blanker(
  286.         logger,
  287.         posts,
  288.         username=sys.argv[2],
  289.         password=getpass.getpass(
  290.             "Enter the password for user '{}': ".format(
  291.                 sys.argv[2]))
  292.     )
  293.     blanker.run()
  294.  
  295.  
  296. if __name__ == "__main__":
  297.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement