(\S+)
', page) 126 | remarks = re.search('(.*?)
(.*?)
', page) 127 | 128 | try: 129 | remarks = remarks.groups() 130 | except: 131 | return jenni.say('Could not find relevant information.') 132 | 133 | resp = str() 134 | resp += '1 BTC == %s USD. ' % price.groups() 135 | 136 | if remarks: 137 | resp += '%s %s' % (remarks[0], remarks[1]) 138 | 139 | jenni.say(resp) 140 | 141 | fbtc.commands = ['fbtc'] 142 | fbtc.example = '.fbtc' 143 | fbtc.rate = 20 144 | 145 | 146 | def bitfinex_retrieve(page): 147 | bitfinex_json = json.loads(page) 148 | return bitfinex_json[-4] 149 | 150 | 151 | def bcc(jenni, input): 152 | '''.bcc -- display the current prices for Bitcoin Cash''' 153 | global last_check_bcc 154 | global bcc_exchange_rates 155 | 156 | now = dt.datetime.now() 157 | response = '\x02\x0303One (1) Bitcoin Cash (BCH) in USD:\x03\x02 ' 158 | 159 | bcc_exchanges = { 160 | 'Bitfinex': ['https://api.bitfinex.com/v2/ticker/tBCHUSD', lambda x: json.loads(x)[-4]], 161 | 'Bittrex': ['https://bittrex.com/api/v1.1/public/getticker?market=USDT-BCC', lambda x: json.loads(x)['result']['Last']], 162 | 'CoinMarketCap': ['https://api.coinmarketcap.com/v1/ticker/bitcoin-cash/', lambda x: json.loads(x)[0]['price_usd']], 163 | } 164 | 165 | exchanges_status = list() 166 | 167 | if (not bcc_exchange_rates) or (now - last_check_bcc > dt.timedelta(minutes=15)): 168 | 169 | for bcc_exchange in bcc_exchanges: 170 | try: 171 | bcc_page = web.get(bcc_exchanges[bcc_exchange][0]) 172 | except: 173 | exchanges_status.append(False) 174 | continue 175 | 176 | try: 177 | exchange_price = bcc_exchanges[bcc_exchange][1](bcc_page) 178 | except: 179 | exchanges_status.append(False) 180 | continue 181 | 182 | exchanges_status.append(True) 183 | 184 | bcc_exchange_rates[bcc_exchange] = exchange_price 185 | last_check_bcc = dt.datetime.now() 186 | 187 | if not any(exchanges_status): 188 | return jenni.say('We could not access any of the APIs.') 189 | 190 | for exchange in bcc_exchange_rates: 191 | response += '\x1F{0}\x1F: ${1:.2f}, '.format(exchange, round(float(bcc_exchange_rates[exchange]), 2)) 192 | 193 | temp_time = last_check_bcc.strftime('%Y-%m-%d %H:%M') 194 | response += '\x02Last updated\x02 at: {0} UTC'.format(temp_time) 195 | 196 | #print "exchanges_status:", exchanges_status 197 | jenni.say(response) 198 | bcc.commands = ['bcc'] 199 | 200 | 201 | if __name__ == '__main__': 202 | print __doc__.strip() 203 | -------------------------------------------------------------------------------- /modules/chicken_reply.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | chicken_reply.py - Chicken reply Module (Chicken butt, chicken thigh) 5 | Copyright 2015, Kevin Holland (kevinholland94@gmail.com) 6 | Licensed under the Eiffel Forum License 2. 7 | 8 | NOTE: This module should be disabled by default, as it is sort of stupid and could be annoying. 9 | This module will reply "Chicken butt" or "Chicken thigh" respectively to "why" or "what" messages in the channel. 10 | 11 | Example: 12 | person | what? 13 | bot | person: chicken butt 14 | 15 | More info: 16 | * jenni: https://github.com/myano/jenni/ 17 | * Phenny: http://inamidst.com/phenny/ 18 | ''' 19 | 20 | import re 21 | 22 | responses = {'what' : 'chicken butt', 23 | 'why' : 'chicken thigh', 24 | 'wat' : 'chicken bat', 25 | 'where': 'chicken hair', 26 | 'when' : 'chicken pen', 27 | 'who' : 'chicken poo', 28 | 'how' : 'chicken plow'} 29 | 30 | def chicken_reply(jenni, input): 31 | question = re.sub('[?!]', '', input.groups()[0]) 32 | message = responses[question.lower()] 33 | message = message.upper() if question.isupper() else message 34 | jenni.reply(message) 35 | 36 | chicken_reply.rule = r'(?i)(^({qs})[\?\!]*$)'.format(qs="|".join(responses.keys())) 37 | chicken_reply.priority = 'low' 38 | chicken_reply.example = 'why?' 39 | chicken_reply.thread = False 40 | 41 | if __name__ == '__main__': 42 | print __doc__.strip() 43 | -------------------------------------------------------------------------------- /modules/codepoints.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | codepoints.py - jenni Codepoints Module 4 | Copyright 2009-2014, yano (yanovich.net) 5 | Copyright 2008-2013, Sean B. Palmer (inamidst.com) 6 | Licensed under the Eiffel Forum License 2. 7 | 8 | More info: 9 | * jenni: https://github.com/myano/jenni/ 10 | * Phenny: http://inamidst.com/phenny/ 11 | """ 12 | 13 | import re, unicodedata 14 | from itertools import islice 15 | import web 16 | import random 17 | 18 | cp_names = dict() 19 | cp_ranges = dict() 20 | data_loaded = False 21 | 22 | 23 | def load_data(): 24 | all_code = web.get('http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt') 25 | for line in all_code.split('\n'): 26 | parts = line.split(';') 27 | if len(parts) >= 10: 28 | name = parts[1] 29 | name_parts = name.split(',') 30 | name_part_tmp = name_parts[0].replace('<', '') 31 | 32 | ## look for codepoint ranges 33 | if 'First>' in name: 34 | if name_part_tmp not in cp_ranges: 35 | cp_ranges[name_part_tmp] = list() 36 | cp_ranges[name_part_tmp].append(parts[0]) 37 | elif 'Last>' in name: 38 | if name_part_tmp not in cp_ranges: 39 | cp_ranges[name_part_tmp] = list() 40 | cp_ranges[name_part_tmp].append(parts[0]) 41 | 42 | if parts[10]: 43 | name += ' ' + str(parts[10]) 44 | 45 | ## remove '<' and '>' from names (usually only on ranges) 46 | name = name.replace('<', '') 47 | name = name.replace('>', '') 48 | cp_names[parts[0]] = name 49 | 50 | ## generate codepoints for ranges founded above 51 | for cp_range in cp_ranges: 52 | cps = cp_ranges[cp_range] 53 | start = cps[0] 54 | end = cps[1] 55 | 56 | for number in xrange(int(start, 16), int(end, 16)): 57 | cp_names['%04X' % (number)] = cp_range 58 | 59 | 60 | def about(u, cp=None, name=None): 61 | global data_loaded 62 | 63 | ## load UnicodeData 64 | if not data_loaded: 65 | load_data() 66 | data_loaded = True 67 | 68 | if cp is None: 69 | ## cp is not provided, we can safely grab the codepoint 70 | cp = ord(u) 71 | else: 72 | ## codepoint is provided but is in hexadeciaml 73 | cp = int(cp, 16) 74 | 75 | if name is None: 76 | name = 'No Name Found' 77 | ## we need the U+XXXX numbers 78 | ## which are hex numbers 79 | ## it is how the numbers are formatted in the UnicodeData file 80 | search_cp = '%04X' % (cp) 81 | if search_cp in cp_names: 82 | name = cp_names[search_cp] 83 | 84 | ## TODO: Replace this... 85 | if not unicodedata.combining(u): 86 | template = 'U+%04X %s (%s)' 87 | else: 88 | template = 'U+%04X %s (\xe2\x97\x8c%s)' 89 | 90 | return template % (cp, name, u.encode('utf-8')) 91 | 92 | 93 | def codepoint_simple(arg): 94 | global data_loaded 95 | 96 | ## load UnicodeData 97 | if not data_loaded: 98 | load_data() 99 | data_loaded = True 100 | 101 | arg = arg.upper() 102 | 103 | r_label = re.compile('\\b' + arg.replace(' ', '.*\\b') + '\\b') 104 | 105 | results = list() 106 | 107 | ## loop over all codepoints that we have 108 | for cp in cp_names: 109 | u = unichr(int(cp, 16)) 110 | name = cp_names[cp] 111 | if r_label.search(name): 112 | results.append((len(name), u, cp, name)) 113 | 114 | if not results: 115 | r_label = re.compile('\\b' + arg.replace(' ', '.*\\b')) 116 | 117 | for cp in cp_names: 118 | u = unichr(int(cp, 16)) 119 | name = cp_names[cp] 120 | if r_label.search(name): 121 | results.append((len(name), u, cp, name)) 122 | 123 | if not results: 124 | return None 125 | 126 | length, u, cp, name = sorted(results)[0] 127 | return about(u, cp, name) 128 | 129 | 130 | def codepoint_extended(arg): 131 | global data_loaded 132 | 133 | ## load UnicodeData 134 | if not data_loaded: 135 | load_data() 136 | data_loaded = True 137 | 138 | arg = arg.upper() 139 | try: r_search = re.compile(arg) 140 | except: raise ValueError('Broken regexp: %r' % arg) 141 | 142 | ## loop over all codepoints that we have 143 | for cp in cp_names: 144 | u = unichr(int(cp, 16)) 145 | name = '-' 146 | name = cp_names[cp] 147 | if r_search.search(name): 148 | yield about(u, cp, name) 149 | 150 | 151 | def u(jenni, input): 152 | '''Look up unicode information.''' 153 | arg = input.bytes[3:] 154 | # jenni.msg('#inamidst', '%r' % arg) 155 | if not arg: 156 | return jenni.reply('You gave me zero length input.') 157 | elif not arg.strip(' '): 158 | if len(arg) > 1: return jenni.reply('%s SPACEs (U+0020)' % len(arg)) 159 | return jenni.reply('1 SPACE (U+0020)') 160 | 161 | # @@ space 162 | if set(arg.upper()) - set( 163 | 'ABCDEFGHIJKLMNOPQRSTUVWYXYZ0123456789- .?+*{}[]\\/^$'): 164 | printable = False 165 | elif len(arg) > 1: 166 | printable = True 167 | else: printable = False 168 | 169 | if printable: 170 | extended = False 171 | for c in '.?+*{}[]\\/^$': 172 | if c in arg: 173 | extended = True 174 | break 175 | 176 | ## allow for codepoints as short as 4 and up to 6 177 | ## since the official spec as of Unicode 7.0 only has 178 | ## hexadeciaml numbers with a length no greater than 6 179 | if 4 <= len(arg) <= 6: 180 | try: u = unichr(int(arg, 16)) 181 | except ValueError: pass 182 | else: return jenni.say(about(u)) 183 | 184 | if extended: 185 | # look up a codepoint with regexp 186 | results = list(islice(codepoint_extended(arg), 4)) 187 | for i, result in enumerate(results): 188 | if (i < 2) or ((i == 2) and (len(results) < 4)): 189 | jenni.say(result) 190 | elif (i == 2) and (len(results) > 3): 191 | jenni.say(result + ' [...]') 192 | if not results: 193 | jenni.reply('Sorry, no results') 194 | else: 195 | # look up a codepoint freely 196 | result = codepoint_simple(arg) 197 | if result is not None: 198 | jenni.say(result) 199 | else: jenni.reply('Sorry, no results for %r.' % arg) 200 | else: 201 | text = arg.decode('utf-8') 202 | if len(text) <= 3: 203 | ## look up less than three podecoints 204 | for u in text: 205 | jenni.say(about(u)) 206 | elif len(text) <= 10: 207 | ## look up more than three podecoints 208 | jenni.reply(' '.join('U+%04X' % ord(c) for c in text)) 209 | else: 210 | ## oh baby 211 | jenni.reply('Sorry, your input is too long!') 212 | u.commands = ['u'] 213 | u.example = '.u 203D' 214 | 215 | 216 | def bytes(jenni, input): 217 | '''Show the input as pretty printed bytes.''' 218 | b = input.bytes 219 | jenni.reply('%r' % b[b.find(' ') + 1:]) 220 | bytes.commands = ['bytes'] 221 | bytes.example = '.bytes \xe3\x8b\xa1' 222 | 223 | if __name__ == '__main__': 224 | print __doc__.strip() 225 | -------------------------------------------------------------------------------- /modules/colours.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | colours.py - jenni Colour Module 4 | Copyright 2015, yano (yanovich.net) 5 | Licensed under the Eiffel Forum License 2. 6 | 7 | More info: 8 | * jenni: https://github.com/myano/jenni/ 9 | * Phenny: http://inamidst.com/phenny/ 10 | """ 11 | 12 | colours = { 13 | "00": "white", 14 | "01": "black", 15 | "02": "blue", 16 | "03": "green", 17 | "04": "light red", 18 | "05": "red", 19 | "06": "magenta (purple)", 20 | "07": "orange", 21 | "08": "yellow", 22 | "09": "light green", 23 | "10": "cyan", 24 | "11": "light cyan", 25 | "12": "light blue", 26 | "13": "light magenta (pink)", 27 | "14": "gray", 28 | "15": "light grey", 29 | "16": "unk", 30 | "17": "unk", 31 | "18": "unk", 32 | "19": "unk", 33 | "20": "unk", 34 | "21": "unk", 35 | "22": "unk", 36 | "23": "unk", 37 | "24": "unk", 38 | } 39 | 40 | 41 | def test_colours(jenni, input): 42 | if not input.admin and input.sender.startswith('#'): 43 | return 44 | output = str() 45 | 46 | keys = colours.keys() 47 | keys.sort() 48 | bold_output = str() 49 | for colour in keys: 50 | output += "\x03{0}{1} ({0})\x03, ".format(colour, colours[colour]) 51 | bold_output += "\x02\x03{0}{1} ({0})\x03\x02, ".format(colour, 52 | colours[colour]) 53 | 54 | output = output[:-2] 55 | bold_output = bold_output[:-2] 56 | 57 | jenni.say(output) 58 | jenni.say(bold_output) 59 | test_colours.commands = ['color', 'colour', 'colors', 'colours'] 60 | test_colours.priority = 'high' 61 | 62 | 63 | if __name__ == '__main__': 64 | print __doc__.strip() 65 | -------------------------------------------------------------------------------- /modules/countdown.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | countdown.py - jenni Countdown Module 4 | Copyright 2011-2013, yano (yanovich.net) 5 | Licensed under the Eiffel Forum License 2. 6 | 7 | More info: 8 | * jenni: https://github.com/myano/jenni/ 9 | * Phenny: http://inamidst.com/phenny/ 10 | """ 11 | 12 | from datetime import datetime, timedelta 13 | 14 | ## TODO: just scrape, https://www.timeanddate.com/countdown/generic?iso=20170411T070001&p0=1440&msg=DO+SFO2+DOWNTIME&ud=1&font=cursive 15 | 16 | bad_format = "Please use correct format: .countdown 2012 12 21 You can also try: '.nye -5'" 17 | ## 2036 02 07 18 | 19 | def get_output(calculate_date, today, nye): 20 | #ending = "%s %s-%s-%sT%s00Z" 21 | verb = str() 22 | if calculate_date <= today: 23 | diff = today - calculate_date 24 | verb = "since" 25 | # if nye: 26 | # return get_output(calculate_date + timedelta(days=365), today, False) 27 | else: 28 | diff = calculate_date - today 29 | verb = "until" 30 | output = str() 31 | mills = 0 32 | centuries = 0 33 | decades = 0 34 | years = 0 35 | days = abs(diff.days) 36 | 37 | unit = str() 38 | if days > 365250: 39 | mills = diff.days / 365250 40 | days -= mills * 365250 41 | 42 | if mills == 1: unit = "millennium" 43 | else: unit = "millenniums" 44 | if mills: 45 | output += "%s %s, " % (str(mills), unit) 46 | if days > 36525: 47 | centuries = days / 36525 48 | days -= centuries * 36525 49 | 50 | if centuries == 1: unit = "century" 51 | else: unit = "centuries" 52 | if centuries: 53 | output += "%s %s, " % (str(centuries), unit) 54 | if days > 3652: 55 | decades = days / 3652 56 | days -= decades * 3652 57 | 58 | if decades == 1: unit = "decade" 59 | else: unit = "decades" 60 | if decades: 61 | output += "%s %s, " % (str(decades), unit) 62 | if days > 365: 63 | years = days / 365 64 | days -= years * 365 65 | 66 | if years == 1: unit = "year" 67 | else: unit = "years" 68 | if years: 69 | output += "%s %s, " % (str(years), unit) 70 | 71 | if days: 72 | if days == 1: unit = "day" 73 | else: unit = "days" 74 | output += "%s %s, " % (str(days), unit) 75 | 76 | hours = diff.seconds / 3600 77 | if hours: 78 | if hours == 1: unit = "hour" 79 | else: unit = "hours" 80 | output += "%s %s, " % (str(hours), unit) 81 | 82 | minutes = (diff.seconds/60 - hours * 60) 83 | if minutes: 84 | if minutes > 1: unit = "minutes" 85 | elif minutes == 1: unit = "minute" 86 | output += "%s %s, " % (str(minutes), unit) 87 | 88 | seconds = (diff.seconds/60.0 - hours * 60) - (diff.seconds/60 - hours * 60) 89 | seconds *= 60.0 90 | seconds = int(seconds) 91 | if seconds: 92 | if seconds > 1: unit = 'seconds' 93 | elif seconds == 1: unit = 'second' 94 | output += '%s %s, ' % (str(seconds), unit) 95 | 96 | if output and output[0] == "-": 97 | output = output[1:] 98 | 99 | #output += ending % (verb, year.zfill(4), month.zfill(2), day.zfill(2), offset.zfill(2)) 100 | return '%s%s' % (output, verb) 101 | 102 | 103 | def two(inc): 104 | return str(inc).zfill(2) 105 | def three(inc): 106 | return str(inc).zfill(3) 107 | 108 | 109 | def generic_countdown(jenni, input): 110 | """ .countdown(.*?)
\n)')
27 | r_tag = re.compile(r'(?ims)<[^>]+>')
28 | r_anchor = re.compile(r'(?ims)( .+TWSS\.* ]*>.*?.*?
')
17 |
18 |
19 | def getwhy(jenni, input):
20 | page = web.get(whyuri)
21 | paragraphs = r_paragraph.findall(page)
22 | out = str()
23 | if paragraphs:
24 | line = re.sub(r'<[^>]*?>', '', unicode(paragraphs[0]))
25 | out = line.lower().capitalize() + "."
26 | else:
27 | out = 'We are unable to find any reasons *why* this should work.'
28 |
29 | return jenni.say(out)
30 | getwhy.commands = ['why', 'tubbs']
31 | getwhy.thread = False
32 | getwhy.rate = 30
33 |
34 | if __name__ == '__main__':
35 | print __doc__.strip()
36 |
--------------------------------------------------------------------------------
/modules/wikipedia.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """
3 | wikipedia.py - jenni Wikipedia Module
4 | Copyright 2009-2013, yano (yanovich.net)
5 | Copyright 2008-2013, Sean B. Palmer (inamidst.com)
6 | Licensed under the Eiffel Forum License 2.
7 |
8 | More info:
9 | * jenni: https://github.com/myano/jenni/
10 | * Phenny: http://inamidst.com/phenny/
11 | """
12 |
13 | import re, urllib, gzip, StringIO
14 | import web
15 |
16 | wikiuri = 'https://%s.wikipedia.org/wiki/%s'
17 | # wikisearch = 'http://%s.wikipedia.org/wiki/Special:Search?' \
18 | # + 'search=%s&fulltext=Search'
19 |
20 | r_tr = re.compile(r'(?ims)]*>.*? ')
21 | r_paragraph = re.compile(r'(?ims)