/modules/calc.py
Python | 266 lines | 208 code | 25 blank | 33 comment | 27 complexity | eb5cfd5d07c0051ebdf5457eae23c63e MD5 | raw file
1#!/usr/bin/env python 2# coding=utf-8 3""" 4calc.py - jenni Calculator Module 5Copyright 2009-2013, Michael Yanovich (yanovich.net) 6Copyright 2008-2013, Sean B. Palmer (inamidst.com) 7Licensed under the Eiffel Forum License 2. 8 9More info: 10 * jenni: https://github.com/myano/jenni/ 11 * Phenny: http://inamidst.com/phenny/ 12""" 13 14import HTMLParser 15import json 16import re 17import string 18import urllib 19import web 20 21from modules import search 22from modules import unicode as uc 23from socket import timeout 24 25 26c_pattern = r'(?ims)<(?:h2 class="r"|div id="aoba")[^>]*>(.*?)</(?:h2|div)>' 27c_answer = re.compile(c_pattern) 28r_tag = re.compile(r'<(?!!)[^>]+>') 29 30try: 31 from modules import proxy 32except: 33 pass 34 35 36def clean_up_answer(answer): 37 answer = answer.encode('utf-8') 38 answer = answer.decode('utf-8') 39 answer = ''.join(chr(ord(c)) for c in answer) 40 answer = uc.decode(answer) 41 answer = answer.replace('<sup>', '^(') 42 answer = answer.replace('</sup>', ')') 43 answer = web.decode(answer) 44 answer = answer.strip() 45 answer += ' [GC]' 46 return answer 47 48 49def c(jenni, input): 50 '''.c -- Google calculator.''' 51 52 ## let's not bother if someone doesn't give us input 53 if not input.group(2): 54 return jenni.reply('Nothing to calculate.') 55 56 ## handle some unicode conversions 57 q = input.group(2).encode('utf-8') 58 q = q.replace('\xcf\x95', 'phi') # utf-8 U+03D5 59 q = q.replace('\xcf\x80', 'pi') # utf-8 U+03C0 60 temp_q = q.replace(' ', '') 61 62 ## Attempt #1 (Google) 63 uri_base = 'http://www.google.com/search?gbv=1&q=' 64 uri = uri_base + web.urllib.quote(temp_q) 65 66 ## To the webs! 67 page = str() 68 try: 69 page = proxy.get(uri) 70 except: 71 ## if we can't access Google for calculating 72 ## let us move on to Attempt #2 73 page = web.get(uri) 74 75 answer = False 76 if page: 77 ## if we get a response from Google 78 ## let us parse out an equation from Google Search results 79 answer = c_answer.findall(page) 80 81 if answer: 82 ## if the regex finding found a match we want the first result 83 answer = answer[0] 84 answer = clean_up_answer(answer) 85 jenni.say(answer) 86 else: 87 #### Attempt #1a 88 uri = uri_base + web.urllib.quote(q) 89 try: 90 page = proxy.get(uri) 91 except: 92 page = web.get(uri) 93 94 answer = False 95 96 if page: 97 answer = c_answer.findall(page) 98 if answer: 99 answer = answer[0] 100 answer = clean_up_answer(answer) 101 jenni.say(answer) 102 else: 103 104 #### Attempt #2 105 attempt_two = False 106 try: 107 from BeautifulSoup import BeautifulSoup 108 attempt_two = True 109 except: 110 attempt_two = False 111 112 output = str() 113 """ 114 if attempt_two: 115 new_url = 'https://duckduckgo.com/html/?q=%s&kl=us-en&kp=-1' % (web.urllib.quote(q)) 116 try: 117 ddg_html_page = proxy.get(new_url) 118 except: 119 ddg_html_page = web.get(new_url) 120 soup = BeautifulSoup(ddg_html_page) 121 122 ## use BeautifulSoup to parse HTML for an answer 123 zero_click = str() 124 if soup('div', {'class': 'zero-click-result'}): 125 zero_click = str(soup('div', {'class': 'zero-click-result'})[0]) 126 127 ## remove some excess text 128 output = r_tag.sub('', zero_click).strip() 129 output = output.replace('\n', '').replace('\t', '') 130 131 ## test to see if the search module has 'remove_spaces' 132 ## otherwise, let us fail 133 try: 134 output = search.remove_spaces(output) 135 except: 136 output = str() 137 """ 138 output = False 139 140 if output: 141 ## If Attempt #2 worked, display the answer 142 jenni.say(output + ' [DDG HTML]') 143 144 else: 145 #### Attempt #3 (DuckDuckGo's API) 146 ddg_uri = 'https://api.duckduckgo.com/?format=json&q=' 147 ddg_uri += urllib.quote(q) 148 149 ## Try to grab page (results) 150 ## If page can't be accessed, we shall fail! 151 try: 152 page = proxy.get(ddg_uri) 153 except: 154 page = web.get(ddg_uri) 155 156 ## Try to take page source and json-ify it! 157 try: 158 json_response = json.loads(page) 159 except: 160 ## if it can't be json-ified, then we shall fail! 161 json_response = None 162 163 ## Check for 'AnswerType' (stolen from search.py) 164 ## Also 'fail' to None so we can move on to Attempt #3 165 if (not json_response) or (hasattr(json_response, 'AnswerType') and json_response['AnswerType'] != 'calc'): 166 answer = None 167 else: 168 ## If the json contains an Answer that is the result of 'calc' 169 ## then continue 170 answer = json_response['Answer'] 171 if hasattr(answer, 'result'): 172 answer = answer['result'] 173 parts = answer.split('</style>') 174 answer = ''.join(parts[1:]) 175 answer = re.sub(r'<.*?>', '', answer).strip() 176 177 if answer: 178 ## If we have found answer with Attempt #2 179 ## go ahead and display it 180 answer += ' [DDG API]' 181 jenni.say(answer) 182 183 else: 184 #### Attempt #4 (DuckDuckGo's HTML) 185 ## This relies on BeautifulSoup; if it can't be found, don't even bother 186 187 #### Attempt #3 (Wolfram Alpha) 188 status, answer = get_wa(q) 189 190 if status: 191 jenni.say(answer + ' [WA]') 192 else: 193 ## If we made it this far, we have tried all available resources 194 jenni.say('Absolutely no results!') 195c.commands = ['c', 'cal', 'calc'] 196c.example = '.c 5 + 3' 197 198 199def py(jenni, input): 200 """.py <code> -- evaluates python code""" 201 code = input.group(2) 202 if not code: 203 return jenni.reply('No code provided.') 204 query = code.encode('utf-8') 205 uri = 'https://tumbolia-two.appspot.com/py/' 206 try: 207 answer = web.get(uri + web.urllib.quote(query)) 208 if answer is not None and answer != "\n": 209 jenni.say(answer) 210 else: 211 jenni.reply('Sorry, no result.') 212 except Exception, e: 213 jenni.reply('The server did not return an answer.') 214py.commands = ['py', 'python'] 215py.example = '.py print "Hello world, %s!" % ("James")' 216 217 218def get_wa(search): 219 txt = search 220 txt = txt.decode('utf-8') 221 txt = txt.encode('utf-8') 222 query = txt 223 uri = 'https://tumbolia-two.appspot.com/wa/' 224 uri += urllib.quote(query.replace('+', '%2B')) 225 answer = web.get(uri) 226 if answer: 227 answer = answer.decode("string_escape") 228 answer = HTMLParser.HTMLParser().unescape(answer) 229 match = re.search('\\\:([0-9A-Fa-f]{4})', answer) 230 if match is not None: 231 char_code = match.group(1) 232 char = unichr(int(char_code, 16)) 233 answer = answer.replace('\:' + char_code, char) 234 waOutputArray = string.split(answer, ";") 235 newOutput = list() 236 for each in waOutputArray: 237 temp = each.replace('\/', '/') 238 newOutput.append(temp) 239 waOutputArray = newOutput 240 if (len(waOutputArray) < 2): 241 return True, answer 242 else: 243 return True, waOutputArray[0] + ' | ' + ' | '.join(waOutputArray[1:4]) 244 waOutputArray = list() 245 else: 246 return False, str() 247 248 249def wa(jenni, input): 250 """.wa <input> -- queries WolframAlpha with the given input.""" 251 if not input.group(2): 252 return jenni.reply("No search term.") 253 txt = input.group(2) 254 txt = txt.encode('utf-8') 255 txt = txt.decode('utf-8') 256 txt = txt.encode('utf-8') 257 status, answer = get_wa(txt) 258 if status: 259 jenni.say(answer) 260 else: 261 jenni.say('Sorry, no result from WolframAlpha.') 262wa.commands = ['wa', 'wolfram'] 263wa.example = '.wa land area of the European Union' 264 265if __name__ == '__main__': 266 print __doc__.strip()