PageRenderTime 3ms CodeModel.GetById 2ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/calc.py

https://github.com/myano/jenni
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()