askcompu

Untitled

Jul 21st, 2015
330
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.98 KB | None | 0 0
  1. #!/usr/bin/env python
  2. '''
  3. head.py - Phenny HTTP Metadata Utilities
  4. Copyright 2008, Sean B. Palmer, inamidst.com
  5. Licensed under the Eiffel Forum License 2.
  6.  
  7. http://inamidst.com/phenny/
  8.  
  9. Modified by Jordan Kinsley <[email protected]>
  10. '''
  11.  
  12. import re, os.path, json, imp
  13. import urllib.request
  14. import urllib.parse
  15. import urllib.error
  16. import http.client
  17. import http.cookiejar
  18. import time
  19. from datetime import timedelta
  20. from html.entities import name2codepoint
  21. import web
  22. from tools import deprecated
  23.  
  24. cj = http.cookiejar.LWPCookieJar(os.path.join(os.path.expanduser('~/.phenny'), 'cookies.lwp'))
  25. opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
  26. urllib.request.install_opener(opener)
  27.  
  28. def head(phenny, input):
  29.     """Provide HTTP HEAD information."""
  30.     uri = input.group(2)
  31.     uri = (uri or '')
  32.     if ' ' in uri:
  33.         uri, header = uri.rsplit(' ', 1)
  34.     else: uri, header = uri, None
  35.  
  36.     if not uri and hasattr(phenny, 'last_seen_uri'):
  37.         try: uri = phenny.last_seen_uri[input.sender]
  38.         except KeyError: return phenny.say('?')
  39.  
  40.     if not uri.startswith('htt'):
  41.         uri = 'http://' + uri
  42.     # uri = uri.replace('#!', '?_escaped_fragment_=')
  43.    
  44.     start = time.time()
  45.  
  46.     try:
  47.         info = web.head(uri)
  48.         info['status'] = '200'
  49.     except urllib.error.HTTPError as e:
  50.         return phenny.say(str(e.code))
  51.     except http.client.InvalidURL:
  52.         return phenny.say("Not a valid URI, sorry.")
  53.     except IOError:
  54.         return phenny.say("Can't connect to %s" % uri)
  55.  
  56.     resptime = time.time() - start
  57.  
  58.     if header is None:
  59.         data = []
  60.         if 'Status' in info:
  61.             data.append(info['Status'])
  62.         if 'content-type' in info:
  63.             data.append(info['content-type'].replace('; charset=', ', '))
  64.         if 'last-modified' in info:
  65.             modified = info['last-modified']
  66.             modified = time.strptime(modified, '%a, %d %b %Y %H:%M:%S %Z')
  67.             data.append(time.strftime('%Y-%m-%d %H:%M:%S UTC', modified))
  68.         if 'content-length' in info:
  69.             data.append(info['content-length'] + ' bytes')
  70.         data.append('{0:1.2f} s'.format(resptime))
  71.         phenny.reply(', '.join(data))
  72.     else:
  73.         headerlower = header.lower()
  74.         if headerlower in info:
  75.             phenny.say(header + ': ' + info.get(headerlower))
  76.         else:
  77.             msg = 'There was no %s header in the response.' % header
  78.             phenny.say(msg)
  79. head.commands = ['head']
  80. head.example = '.head http://www.w3.org/'
  81.  
  82. r_title = re.compile(r'(?ims)<title[^>]*>(.*?)</title\s*>')
  83. r_entity = re.compile(r'&[A-Za-z0-9#]+;')
  84.  
  85. @deprecated
  86. def f_title(self, origin, match, args):
  87.     """.title <URI> - Return the title of URI."""
  88.     uri = match.group(2)
  89.     uri = (uri or '')
  90.  
  91.     if not uri and hasattr(self, 'last_seen_uri'):
  92.         uri = self.last_seen_uri.get(origin.sender)
  93.     if not uri:
  94.         return self.msg(origin.sender, 'I need a URI to give the title of...')
  95.     title = gettitle(uri)
  96.     if title:
  97.         self.msg(origin.sender, origin.nick + ': ' + title)
  98.     else: self.msg(origin.sender, origin.nick + ': No title found')
  99. f_title.commands = ['title']
  100.  
  101. def noteuri(phenny, input):
  102.     uri = input.group(1)
  103.     if not hasattr(phenny.bot, 'last_seen_uri'):
  104.         phenny.bot.last_seen_uri = {}
  105.     phenny.bot.last_seen_uri[input.sender] = uri
  106. noteuri.rule = r'.*(http[s]?://[^<> "\x01]+)[,.]?'
  107. noteuri.priority = 'low'
  108.  
  109. titlecommands = r'(?:' + r'|'.join(f_title.commands) + r')'
  110. def snarfuri(phenny, input):
  111.     if re.match(r'(?i)' + phenny.config.prefix + titlecommands, input.group()):
  112.         return
  113.     uri = input.group(1)
  114.     if input.nick in ('derpy','Chance'):
  115.         return
  116.     try:
  117.         if re.compile('http(s)?://(.*).(jpg|jpeg|png|gif|tiff|bmp)').match(uri):
  118.             return None
  119.        
  120.         title = None
  121.  
  122.         youtube = re.compile('http(s)?://(www.)?youtube.(com|co.uk|ca)?/watch.*\?.*v\=\w+')
  123.         if youtube.match(uri) or re.compile('http(s)?://youtu.be/(.*)').match(uri):
  124.             # due to changes in how Youtube gives out API access, we need a key from the config file
  125.             if get_youtube_title(uri, phenny.config.youtube_api_key) is None:
  126.                 phenny.say("Sorry " + input.nick + " but you need to fix your URL.")
  127.                 return
  128.             else:
  129.                 title = get_youtube_title(uri, phenny.config.youtube_api_key)
  130.  
  131.         fimfiction = re.compile('http(s)?://(www.)?fimfiction.net/story/')
  132.         if fimfiction.match(uri):
  133.             title = get_story_title(uri)
  134.  
  135.         if re.compile('http(s)?://(www.)?((e621)|(e926)).net/post/show/').match(uri): #e621 or e926 link
  136.             title = ouroboros('e621',uri)
  137.  
  138.         if re.compile('http(s)?://(www.)?twentypercentcooler.net/post/show/').match(uri):
  139.             title = ouroboros('twentypercentcooler',uri)
  140.  
  141.         if re.compile('http(s)?://(www.)?derpiboo((.ru)|(ru.org))(/images)?/').match(uri):
  142.             title = derpibooru(uri)
  143.  
  144.         if title:
  145.             phenny.msg(input.sender, '[ ' + title + ' ]')
  146.         else:
  147.             title = gettitle(uri)
  148.             if title: phenny.msg(input.sender, '[ ' + title + ' ]')
  149.     except http.client.HTTPException:
  150.         return
  151. snarfuri.rule = r'.*(http[s]?://[^<> "\x01]+)[,.]?'
  152. snarfuri.priority = 'low'
  153.  
  154. def gettitle(uri):
  155.     if not ':' in uri:
  156.         uri = 'http://' + uri
  157.     uri = uri.replace('#!', '?_escaped_fragment_=')
  158.  
  159.     title = None
  160.     localhost = [
  161.         'http://localhost/', 'http://localhost:80/',
  162.         'http://localhost:8080/', 'http://127.0.0.1/',
  163.         'http://127.0.0.1:80/', 'http://127.0.0.1:8080/',
  164.         'https://localhost/', 'https://localhost:80/',
  165.         'https://localhost:8080/', 'https://127.0.0.1/',
  166.         'https://127.0.0.1:80/', 'https://127.0.0.1:8080/',
  167.     ]
  168.     for s in localhost:
  169.         if uri.startswith(s):
  170.             return
  171.  
  172.     if re.compile('http(s)?://(www.)?bad-dragon.com/').match(uri) and not check_cookie('baddragon_age_checked'):
  173.         urllib.request.urlopen('http://bad-dragon.com/agecheck/accept')
  174.  
  175.     try:
  176.         redirects = 0
  177.         while True:
  178.             info = web.head(uri)
  179.  
  180.             if not isinstance(info, list):
  181.                 status = '200'
  182.             else:
  183.                 status = str(info[1])
  184.                 info = info[0]
  185.             if status.startswith('3'):
  186.                 uri = urllib.parse.urljoin(uri, info['Location'])
  187.             else: break
  188.  
  189.             redirects += 1
  190.             if redirects >= 25:
  191.                 return None
  192.  
  193.         try: mtype = info['content-type']
  194.         except:
  195.             return None
  196.  
  197.         try:
  198.             # Occasionally throws type errors if a CSS file is given.
  199.             if not (('/html' in mtype) or ('/xhtml' in mtype)):
  200.                 return None
  201.         except:
  202.             return None
  203.  
  204.         bytes = web.get(uri)
  205.         #bytes = u.read(262144)
  206.         #u.close()
  207.  
  208.     except IOError:
  209.         return
  210.     except UnicodeError:
  211.         '''
  212.        Due to the way Python implemented the urllib.request.urlopen()
  213.        function, it is not possible to correct for Unicode characters
  214.        like € in a URL. Therefore, we just catch the error and don't
  215.        provide a title for the link. Other options may be worth
  216.        exploring, and could be included here.
  217.        '''
  218.         return
  219.  
  220.     m = r_title.search(bytes)
  221.     if m:
  222.         title = m.group(1)
  223.         title = title.strip()
  224.         title = title.replace('\t', ' ')
  225.         title = title.replace('\r', ' ')
  226.         title = title.replace('\n', ' ')
  227.         while '  ' in title:
  228.             title = title.replace('  ', ' ')
  229.         if len(title) > 200:
  230.             title = title[:200] + '[...]'
  231.        
  232.         def e(m):
  233.             entity = m.group(0)
  234.             if entity.startswith('&#x'):
  235.                 cp = int(entity[3:-1], 16)
  236.                 return chr(cp)
  237.             elif entity.startswith('&#'):
  238.                 cp = int(entity[2:-1])
  239.                 return chr(cp)
  240.             else:
  241.                 char = name2codepoint[entity[1:-1]]
  242.                 return chr(char)
  243.         title = r_entity.sub(e, title)
  244.  
  245.         if title:
  246.             title = title.replace('\n', '')
  247.             title = title.replace('\r', '')
  248.         else: title = None
  249.     return title
  250.  
  251. def check_cookie(name):
  252.     ''' checks the given name against the names of cookies in the cookie
  253.    jar; returns true iff it finds an exact match.'''
  254.     for cookie in cj:
  255.         if cookie.name == name:
  256.             return True
  257.         else: continue
  258.     return False
  259.  
  260. def query(vid, auth_key):
  261.     '''
  262.    returns the title, viewcount, time, and uploader of a Youtube video.
  263.    vid is the Youtube video ID at the end of the Youtube URL.
  264.    '''
  265.    
  266.     main = 'https://www.googleapis.com/youtube/v3/videos?id='
  267.     key = '&key=' + auth_key
  268.     ext = '&part=snippet,contentDetails,statistics'
  269.    
  270.     req = web.get(main + vid + key + ext)
  271.     data = json.loads(req, encoding='utf-8')
  272.     try:
  273.         data = data['items'][0]
  274.     except IndexError: return None
  275.     title = data['snippet']['title']
  276.     uploader = data['snippet']['channelTitle']
  277.     try:
  278.         viewcount = str(data['statistics']['viewCount'])
  279.     except (KeyError, IndexError):
  280.         viewcount = '0'
  281.     duration = str(data['contentDetails']['duration'])
  282.     # a video with no likes results in IndexErrors (assuming true for ratingCount, too)
  283.     try:
  284.         likes = str(data['statistics']['likeCount'])
  285.     except (KeyError, IndexError):
  286.         likes = '0'
  287.     try:
  288.         dislikes = str(data['statistics']['dislikeCount'])
  289.     except (KeyError, IndexError):
  290.         dislikes = '0'
  291.    
  292.     time = iso_8601(duration)
  293.    
  294.     return title, viewcount, time, uploader, likes, dislikes
  295.    
  296. def iso_8601(str_time):
  297.     '''
  298.    Google's Youtube data V3 API gives duration in an ISO 8601 format, e.g.
  299.    P3DT4H30M44S. Each format of time, i.e. 3D, 4H, 30M, 44S are optional
  300.    in the string. P1D is just as valid as PT24H, for instance (and is the
  301.    actual value returned for a video exactly 24 hours, 0 minutes, and 0 seconds long
  302.    
  303.    returns a string of the time, formatted like 3d 4h 30m 44s
  304.    '''
  305.    
  306.     iso_8601_re = re.compile('P(?P<year>[0-9]*Y)?(?P<month>[0-9]*M)?(?P<week>[0-9]*W)?(?P<day>[0-9]*D)?(T)?(?P<hour>[0-9]*H)?(?P<minute>[0-9]*M)?(?P<second>[0-9]*S)?')
  307.    
  308.     found_time = re.match(iso_8601_re, str_time)
  309.     year, month, week, day, hour, minute, second = '','','','','','',''
  310.     return_time = ''
  311.     if found_time.group('year'):
  312.         year = found_time.group('year').rstrip('Y')
  313.         return_time = return_time + year + 'y '
  314.     if found_time.group('month'):
  315.         month = found_time.group('month').rstrip('M')
  316.         return_time = return_time + month + 'm '
  317.     if found_time.group('week'):
  318.         week = found_time.group('week').rstrip('W')
  319.         return_time = return_time + week + 'w '
  320.     if found_time.group('day'):
  321.         day = found_time.group('day').rstrip('D')
  322.         return_time = return_time + day + 'd '
  323.     if found_time.group('hour'):
  324.         hour = found_time.group('hour').rstrip('H')
  325.         return_time = return_time + hour + 'h '
  326.     if found_time.group('minute'):
  327.         minute = found_time.group('minute').rstrip('M')
  328.         return_time = return_time + minute + 'm '
  329.     if found_time.group('second'):
  330.         second = found_time.group('second').rstrip('S')
  331.         return_time = return_time + second + 's '
  332.     return return_time
  333.  
  334. def get_youtube_title(uri, auth_key):
  335.     vid = None
  336.     if 'youtu.be/' not in uri:
  337.         if '?v=' in uri:
  338.             vid = uri[uri.index('?v=')+3:uri.index('?v=') + 14]
  339.         elif '&v=' in uri:
  340.             vid = uri[uri.index('&v=')+3:uri.index('&v=') + 14]
  341.         else:
  342.             return None
  343.     else:
  344.         vid = uri[uri.rindex('be/')+3:uri.rindex('be/')+14]
  345.  
  346.     video_data = query(vid, auth_key)
  347.     if video_data is None:
  348.       return None
  349.     else:
  350.       title, views, time, uploader, likes, dislikes = video_data
  351.     if title == '':
  352.         return None
  353.     percentage = get_percentage(likes, dislikes)
  354.     # Not including the uploader in the title info; it's rarely important in determining a link's quality.
  355.     return "\002You\00300,04Tube\017 " + title + " - " + views + " views - Uploaded by " + uploader + " - " + time + "long - " + likes + " likes - " + dislikes + " dislikes - " + percentage + "%"
  356.  
  357. def get_api_story_title(uri):
  358.     story_id = uri.split('story/')[1]
  359.     if story_id.find('/') > 1:
  360.         story_id = story_id.split('/')[0]
  361.     data = web.get('http://fimfiction.net/api/story.php?story=' + story_id)
  362.     story = json.loads(data, encoding='utf-8')['story']
  363.    
  364.     story_title = format_title(story['title'])
  365.     likes = str(story['likes'])
  366.     dislikes = str(story['dislikes'])
  367.     percentage = get_percentage(likes, dislikes)
  368.     author = story['author']['name'].strip('"')
  369.     views = str(story['views'])
  370.     words = str(story['words'])
  371.     content_rating = int(story['content_rating'])
  372.     chapters = str(story['chapter_count'])
  373.     categories = ''
  374.    
  375.     cat_dict = story['categories']
  376.     for k in cat_dict:
  377.         if cat_dict[k] is True:
  378.             categories = categories + '[' + k + ']'
  379.     return story_title, likes, dislikes, percentage, author, views, words, content_rating, chapters, categories
  380.    
  381. def format_title(title):
  382.     title = title.strip('"')
  383.     title = title.replace('&#039;','\'')
  384.     title = title.replace('&amp;','&')
  385.     title = title.replace('&quot;','"')
  386.     title = title.replace('&lt;','<')
  387.     title = title.replace('&gt;','>')
  388.     return title
  389.    
  390. def get_percentage(likes, dislikes):
  391.     percentage = ''
  392.     if int(likes) > 0 or int(dislikes) > 0:
  393.         percentage = (float(likes) / (float(dislikes) + float(likes))) * 100
  394.         percentage = str(round(percentage, 2))
  395.     else:
  396.         # no likes and dislikes
  397.         percentage = '0.00'
  398.     return percentage
  399.  
  400. def ouroboros(site, uri):
  401.     # e621 and twentypercentcooler use the same software
  402.     # TODO: load tag file; compare tags, generate title
  403.     #load a list of unimportant tags from a file. possible regex?
  404.     title = ''
  405.     def get_id(link):
  406.         exp = '(.*)show/(?P<id>[0-9]*)/?'
  407.         return re.search(exp, link).group('id')
  408.     post_id = get_id(uri)
  409.     json_data = web.get('https://{0}.net/post/show.json?id={1}'.format(site, post_id))
  410.     postdata = json.loads(json_data, encoding='utf-8')
  411.     tags = postdata['tags']
  412.  
  413.     ratings = { 's' : 'Safe', 'q' : 'Questionable', 'e' : 'Explicit' }
  414.     if postdata['rating'] in ratings:
  415.         rating = ratings[postdata['rating']]
  416.     else:
  417.         rating = 'Unknown'
  418.     #compare tags to a list of uniportant tags and drop some/most
  419.     tag_file = os.path.expanduser('~/.phenny/boru.py')
  420.     try:
  421.         boru = imp.load_source('boru',tag_file)
  422.     except Exception as e:
  423.         print("Error loading ignore tags: {0} (in head.py)".format(e))
  424.         filtered = tags
  425.     else:
  426.         filtered = re.sub("\\b(("+")|(".join(boru.ignore_tags)+"))\\b","",tags)
  427.         filtered = re.sub(" +"," ",filtered).strip()
  428.     title = re.sub('_'," ",filtered)
  429.     title = '{0} {1}'.format(rating.capitalize(),title)
  430.     return title
  431.  
  432. def derpibooru(uri):
  433.     # TODO: research derpibooru's API and get data
  434.     def get_id(link):
  435.         exp = '(.*)derpiboo((.ru)|(ru.org))(/images)?/(?P<id>[0-9]*)/?'
  436.         return re.search(exp, link).group('id')
  437.     id = get_id(uri)
  438.     if not id:
  439.         return gettitle(uri)
  440.     json_data = web.get('http://derpiboo.ru/{0}.json'.format(id))
  441.     postdata = json.loads(json_data, encoding='utf-8')
  442.     tags = postdata['tags'].split(', ')
  443.    
  444.     artists = []
  445.     for tag in tags:
  446.         if tag.startswith('artist:'):
  447.             artists.append(tag.replace('artist:', '', 1))
  448.     if artists:
  449.         artists = ' by '+', '.join(artists)
  450.     else:
  451.         artists = ''
  452.     # ratings are tags on Derpibooru
  453.     ratings = []
  454.     for tag in tags:
  455.         if tag in {'explicit', 'grimdark', 'grotesque', 'questionable', 'safe', 'semi-grimdark', 'suggestive'}:
  456.             ratings.append(tag)
  457.     if not ratings:
  458.         ratings = ['unknown']
  459.     ratings = ' '.join(ratings)
  460.     tag_file = os.path.expanduser('~/.phenny/boru.py')
  461.     try:
  462.         boru = imp.load_source('boru',tag_file)
  463.     except Exception as e:
  464.         print("Error loading ignore tags: %s (in head.py)" %(e))
  465.     else:
  466.         tags = [tag for tag in tags if tag not in boru.ignore_tags]
  467.     tag_string = ' '.join(tag.replace(' ', '_') for tag in tags)
  468.     title = '{0} {1}'.format(ratings.title(),tag_string,artists)
  469.     return title
  470.  
  471. def get_story_title(uri):
  472.     story_title, likes, dislikes, percentage, author, views, words, content_rating, chapters, categories = get_api_story_title(uri)
  473.     title = ''
  474.     if content_rating > 1:
  475.         title = title + '\u0002!!*NSFW*!!\u000F - '
  476.     title = title + story_title + " by " + author
  477.     if chapters:
  478.         title = title + ' - ' + str(chapters)
  479.         if int(chapters) > 1:
  480.             title = title + ' chapters'
  481.         else:
  482.             title = title + ' chapter'
  483.     title = title + " - " + views + " views - " + categories + ' - ' + words + ' words'
  484.     title = title + " - Likes: " + likes + " - Dislikes: " + dislikes + " - " + percentage + "%"
  485.     return title
  486.  
  487. if __name__ == '__main__':
  488.     print(__doc__.strip())
Advertisement
Add Comment
Please, Sign In to add comment