3 from datetime
import datetime
, timedelta
4 from xml
.parsers
.expat
import ExpatError
7 from pseudoclient
.cmd_manager
import *
9 from internets_utils
import *
10 from api
.feed
import FeedError
11 from api
.idlerpg
import IrpgPlayer
12 from api
.quotes
import FmlException
13 from api
.weather
import WeatherException
14 from api
.steam
import SteamException
16 def get_citystate_from_zipcode(self
, zipcode
):
17 """Return [city,state] for the given U.S. zip code (if database has been imported)"""
19 con
= core
.dbpool
.get_connection()
22 cursor
.execute("SELECT city, state FROM zipcode_citystate WHERE zipcode=%s", [int(zipcode
)])
23 city
, state
= cursor
.fetchone()
26 core
.dbpool
.put_connection(con
)
31 # Returns colour coded test of persona state in Steam.
33 def get_personastate_text(self
, state
):
36 return '@c14OFFLINE@c'
50 # user is looking to trade
51 return '@c5LOOKING TO TRADE@c'
53 # user is looking to play
54 return '@c10LOOKING TO PLAY@c'
57 return '@c14UNKNOWN@c'
59 def command_weather(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
60 arg
= self
.get_location(opts
, arg
, channel
, sender
)
65 location
= get_citystate_from_zipcode(self
, arg
)
67 self
.errormsg(channel
, 'zip code not recognised.')
69 city
, state
= location
70 location
= '{city}, {state}, USA'.format(city
=city
, state
=state
)
71 w_state
= state
+ u
', '
73 location
= arg
.strip()
76 w
= self
.weather
.get_conditions(location
)
77 except WeatherException
as exc
:
78 if exc
== 'this key is not valid':
79 self
.elog
.warning('WARNING: OpenWeatherMap API key is not correctly set (%s)' % exc
)
80 self
.errormsg(channel
, 'weather data is temporarily unavailable. Try again later')
82 self
.errormsg(channel
, exc
)
85 self
.errormsg(channel
, e
.msg
)
88 code
= get_tempcolor(w
['temp_c'])
90 self
.msg(channel
, format_weather(code
, u
"""@sep @b{w[city]}{w_state}{w[country]}@b @sep @bConditions@b {w[description]} @sep \
91 @bTemperature@b {tempcolor}{w[temp_c]}C / {w[temp_f]}F@o @sep \
92 @bPressure@b {w[pressure]}mb @sep @bHumidity@b {w[humidity]}% @sep \
93 @bRain@b {w[rain]} @sep \
94 Powered by OpenWeatherMap http://openweathermap.org/city/{w[id]} @sep""".format(w
=w
, tempcolor
=code
, w_state
=w_state
)))
97 def command_forecast(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
98 arg
= self
.get_location(opts
, arg
, channel
, sender
)
103 location
= get_citystate_from_zipcode(self
, arg
)
105 self
.errormsg(channel
, 'zip code not recognised.')
107 city
, state
= location
108 location
= '{city}, {state}, USA'.format(city
=city
, state
=state
)
109 w_state
= state
+ u
', '
111 location
= arg
.strip()
114 w
= self
.weather
.get_forecast(location
)
115 except WeatherException
as exc
:
116 if exc
== 'this key is not valid':
117 self
.elog
.warning('WARNING: OpenWeatherMap API key is not correctly set (%s)' % exc
)
118 self
.errormsg(channel
, 'weather data is temporarily unavailable. Try again later')
120 self
.errormsg(channel
, exc
)
123 self
.errormsg(channel
, e
.msg
)
126 fc
= ' @sep '.join([u
"""@b{day[name]}@b {day[description]} @c04{day[max_c]}C / {day[max_f]}F \
127 @c10{day[min_c]}C / {day[min_f]}F""".format(day
=day
) for day
in w
['days']])
129 self
.msg(channel
, u
'@sep @b{w[city]}{w_state}{w[country]}@b @sep {fc} @sep'.format(w
=w
, fc
=fc
, w_state
=w_state
))
132 def command_register_location(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
137 location
= get_citystate_from_zipcode(self
, arg
)
139 self
.errormsg(channel
, 'zip code not recognised.')
141 city
, state
= location
142 location
= '{city}, {state}, USA'.format(city
=city
, state
=state
)
143 w_state
= state
+ u
', '
145 location
= arg
.strip()
147 w
= self
.weather
.get_conditions(location
)
148 except WeatherException
as exc
:
149 if exc
== 'this key is not valid':
150 self
.elog
.warning('WARNING: OpenWeatherMap API key is not correctly set (%s)' % exc
)
151 self
.errormsg(channel
, 'weather data is temporarily unavailable. Try again later')
153 self
.errormsg(channel
, exc
)
156 self
.errormsg(channel
, e
.msg
)
159 loc_name
= u
'{w[city]}{w_state}{w[country]}'.format(w
=w
, w_state
=w_state
)
161 self
.users
.set(sender
, 'location', arg
)
162 self
.msg(channel
, u
'%s: registered location @b%s@b' % (sender
, loc_name
))
164 def command_bing_translate(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
165 sp
= arg
.split(' ', 2)
168 source
, target
, text
= sp
169 if source
.lower() not in self
.bing
.languages
or target
.lower() not in self
.bing
.languages
:
170 source
= self
.bing
.detect_language(arg
)
172 translation
= self
.bing
.translate(arg
)
174 source
= source
.lower()
175 target
= target
.lower()
176 translation
= self
.bing
.translate(text
, source
, target
)
178 source
= self
.bing
.detect_language(arg
)
180 translation
= self
.bing
.translate(arg
)
182 self
.elog
.warning('WARNING: Bing translate failed: %s' % e
)
183 self
.errormsg(channel
, e
.msg
)
186 self
.msg(channel
, '[t] [from %s] %s' % (source
, translation
))
188 def command_google_search(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
190 result
= self
.google
.search(arg
, userinfo
['ip'] if userinfo
['ip'] != '0' else '255.255.255.255')
192 self
.errormsg(channel
, e
.msg
)
195 if result
['responseStatus'] == 403:
196 self
.elog
.warning('WARNING: Google Search failed: %s' % result
['responseDetails'] if 'responseDetails' in result
else 'unknown error')
197 self
.notice(sender
, 'Google Search is temporarily unavailable. Try again later.')
200 result
= result
['responseData']['results']
202 self
.msg(channel
, '[Google] No results found')
206 self
.msg(channel
, '[Google] @b%(title)s@b <@u%(url)s@u>' % {
207 'title': unescape(json
['titleNoFormatting']),
208 'url': json
['unescapedUrl']})
210 if json
['content'] != '':
211 self
.msg(channel
, '[Google] @bDescription@b: %s' % unescape(json
['content']).replace('<b>', '@b').replace('</b>', '@b'))
213 def command_google_image_search(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
215 result
= self
.google
.image_search(arg
, userinfo
['ip'] if userinfo
['ip'] != '0' else '255.255.255.255')
217 self
.errormsg(channel
, e
.msg
)
220 if result
['responseStatus'] == 403:
221 self
.elog
.warning('WARNING: Google Image Search failed: %s' % result
['responseDetails'] if 'responseDetails' in result
else 'unknown error')
222 self
.notice(sender
, 'Google Search is temporarily unavailable. Try again later.')
225 result
= result
['responseData']['results']
227 self
.msg(channel
, '[Google Image] No results found')
231 self
.msg(channel
, '[Google Image] @b%(title)s@b <@u%(url)s@u>' % {
232 'title': unescape(json
['titleNoFormatting']),
233 'width': json
['width'],
234 'height': json
['height'],
235 'url': json
['unescapedUrl']})
237 self
.msg(channel
, '[Google Image] @bSize@b: %(width)sx%(height)spx%(desc)s' % {
239 'width': json
['width'],
240 'height': json
['height'],
241 'desc': (' - @bDescription@b: %s' % unescape(json
['content']).replace('<b>', '@b').replace('</b>', '@b')) if json
['content'] else ''})
244 def command_calc(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
245 try: # local calculation using PyParsing
246 result
= self
.nsp
.eval(arg
)
247 self
.msg(channel
, '[calc] {} = {}'.format(arg
, result
))
248 except: # just throw it at W|A, hopefully they can get it
250 result
= self
.wolfram
.alpha(arg
)
251 except FeedError
as e
:
252 self
.errormsg(channel
, e
.msg
)
256 self
.msg(channel
, '[W|A] Invalid input.')
258 self
.msg(channel
, u
'[W|A] {r[0]} = {r[1]}'.format(r
=result
))
260 def command_youtube_search(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
262 res
= self
.google
.yt_search(arg
)
264 self
.errormsg(channel
, e
.msg
)
268 self
.msg(channel
, '[YouTube] No results found')
271 self
.msg(channel
, """@sep @bYouTube@b %(title)s @sep @bURL@b %(url)s (%(duration)s) @sep @bViews@b %(views)s @sep \
272 @bRating@b %(rating)s/5 - %(votes)s votes @c3@b[+]@b %(liked)s likes @c4@b[-]@b %(disliked)s dislikes @sep""" % {
273 'title': res
['title'],
274 'url': 'http://www.youtube.com/watch?v=' + res
['id'],
275 'duration': '%s' % format_hms(res
['duration']),
276 'views': format_thousand(res
['view_count']),
277 'rating': round(res
['rating'], 2) if res
['rating'] else 0,
278 'votes': format_thousand(res
['rate_count']) if res
['rate_count'] else 0,
279 'liked': format_thousand(res
['liked']) if res
['liked'] else 0,
280 'disliked': format_thousand(res
['disliked']) if res
['disliked'] else 0
283 def command_dictionary(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
285 results
= self
.wordnik
.definition(arg
)
287 self
.errormsg(channel
, e
.msg
)
291 self
.msg(channel
, '[dictionary] Nothing found')
295 for n
, res
in enumerate(results
, 1):
296 self
.notice(sender
, u
'@sep [{num}/{tot}] @bDefinition@b {res.word} @sep {res.text} @sep'.format(
297 res
=res
, num
=n
, tot
=len(results
)))
298 elif 'number' in opts
:
299 if opts
['number'] - 1 < 0 or opts
['number'] - 1 > len(results
):
300 self
.errormsg(channel
, 'option -n out of range: only %d definitions found.' % len(results
))
303 result
= results
[opts
['number'] - 1]
304 self
.msg(channel
, u
'@sep [{num}/{tot}] @bDefinition@b {res.word} @sep {res.text} @sep'.format(
305 res
=result
, num
=opts
['number'], tot
=len(results
)))
307 for n
, res
in enumerate(results
, 1):
308 self
.msg(channel
, u
'@sep [{num}/{tot}] @bDefinition@b {res.word} @sep {res.text} @sep'.format(
309 res
=res
, num
=n
, tot
=len(results
)))
311 self
.notice(sender
, 'To view all definitions: .dict {res.word} -a. To view the n-th definition: .dict {res.word} -n <number>'.format(res
=res
))
314 def command_urbandictionary(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
315 expr
, sep
, def_id
= arg
.partition('/')
317 res
= self
.urbandictionary
.get_definitions(expr
.strip())
319 self
.errormsg(channel
, e
.msg
)
320 self
.elog
.warning('feed error in .urbandictionary: %s' % e
)
323 if res
['result_type'] == 'no_results' or res
['result_type'] == 'fulltext':
324 self
.errormsg(channel
, 'no results found')
325 elif res
['result_type'] == 'exact':
330 self
.errormsg(channel
, 'invalid definition number')
333 entry
= res
['list'][def_id
- 1]
334 definition
= entry
['definition'].replace('\r\n', ' / ').replace('\n', ' / ')
335 example
= entry
['example'].replace('\r\n', ' / ').replace('\n', ' / ')
336 self
.msg(channel
, u
'@sep [{num}/{total}] {entry[word]} @sep {definition} @sep'.format(
338 total
= len(res
['list']),
340 definition
= definition
if len(definition
) < 200 else definition
[:200] + '...',
342 self
.msg(channel
, u
'@sep @bExample@b %s @sep' % (example
if len(example
) < 280 else example
[:280] + '...'))
344 self
.errormsg(channel
, 'invalid definition number')
346 self
.errormsg(channel
, 'definition id out of range: only %d definitions available' % len(res
['list']))
348 for num
, entry
in enumerate(res
['list'], 1):
350 self
.notice(sender
, 'To view a single definition with a related example, type: @b.u %s /def_number@b. For more definitions, visit: %s' % (expr
, res
['list'][0]['permalink']))
353 definition
= entry
['definition'].replace('\r\n', ' / ').replace('\n', ' / ')
354 self
.msg(channel
, u
'@sep [{num}/{total}] {entry[word]} @sep {definition} @sep'.format(
356 total
= len(res
['list']),
358 definition
= definition
if len(definition
) < 200 else definition
[:200] + '...',
361 self
.msg(channel
, 'An exception occurred and has been reported to the developers. If this error persists please do not use the faulty command until it has been fixed.')
362 self
.elog
.warning('unrecognized result type: %s' % res
['result_type'])
364 def command_imdb(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
366 reply
= self
.imdb
.get(arg
)
368 self
.errormsg(channel
, e
.msg
)
371 self
.errormsg(channel
, 'movie not found')
374 if reply
['Response'] != 'True':
375 self
.msg(channel
, '[imdb] Nothing found')
378 self
.msg(channel
, u
"""@sep @b{r[Title]}@b [{r[Year]}] Rated {r[Rated]} @sep @bRating@b {r[imdbRating]}/10, {r[imdbVotes]} votes @sep \
379 @bGenre@b {r[Genre]} @sep @bDirector@b {r[Director]} @sep @bActors@b {r[Actors]} @sep @bRuntime@b {r[Runtime]} @sep""".format(r
=reply
))
380 self
.msg(channel
, u
'@sep @bPlot@b {r[Plot]} @sep @uhttp://www.imdb.com/title/{r[imdbID]}/@u @sep'.format(r
=reply
))
383 # Registers the user's steam ID and links it to his/her nickname.
385 def command_register_steam(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
388 steam_data
= self
.steam
.find_user(arg
)
389 self
.notice(sender
, u
'Steam ID registered, current personaname: {name}'.format(name
= steam_data
['personaname']))
390 self
.users
.set(sender
, 'steamid', steam_data
['steamid'])
391 except SteamException
as exc
:
392 self
.notice(sender
, 'No user found')
395 # Shows user's online status and what game he/sh is playing.
396 # Game does not show when user's profile is set to private.
398 def command_steam(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
399 steamuser
= self
.get_steamid(opts
, arg
, channel
, sender
)
402 steam_data
= self
.steam
.get_status(steamuser
.steamid
)
404 if steam_data
['communityvisibilitystate'] == 1:
406 self
.notice(sender
, 'Profile is hidden. If you want to use this functionality, set your profile to Public.')
410 steam_games
= self
.steam
.get_games(steamuser
.steamid
)
413 playtime_forever_top
= 0
415 playtime_2weeks_top
= 0
417 message
= u
"""@sep @b{player}@b @sep""".format(player
= steam_data
['personaname'])
419 if steam_games
['game_count'] == 0:
420 # You know, because it's possible
421 message
+= u
""" Does not own any games. @sep"""
422 self
.msg(channel
, message
)
425 for item
in steam_games
['games']:
426 ptf
= item
['playtime_forever']
427 if ptf
> playtime_forever_top
:
428 game_forever
= item
['appid']
429 playtime_forever_top
= ptf
430 playtime_forever
+= ptf
433 ptw
= item
['playtime_2weeks']
434 if ptw
> playtime_2weeks_top
:
435 game_2weeks
= item
['appid']
436 playtime_2weeks_top
= ptw
437 playtime_2weeks
+= ptw
442 message
+= u
""" @bTotal games:@b {total} @sep @bTotal playtime:@b {ftime} hours @sep @bPlaytime last 2 weeks:@b {wtime} hours @sep""".format(
443 total
= steam_games
['game_count'],
444 ftime
= round(playtime_forever
/ 60, 0),
445 wtime
= round(playtime_2weeks
/ 60, 0))
446 if game_forever
!= "":
447 fgame
= self
.steam
.get_game_name(game_forever
)
448 message
+= u
""" @bMost played game:@b {fgame}, {ftime} hours @sep""".format(
450 ftime
= round(playtime_forever_top
/ 60, 0))
451 if game_2weeks
!= "":
452 wgame
= self
.steam
.get_game_name(game_2weeks
)
453 message
+= u
""" @bMost played last 2 weeks:@b {wgame}, {wtime} hours @sep""".format(
455 wtime
= round(playtime_2weeks
/ 60, 0))
456 self
.msg(channel
, message
)
459 message
= u
"""@sep @b{player}@b [{status}] @sep""".format(
460 player
= steam_data
['personaname'],
461 status
= get_personastate_text(self
, steam_data
['personastate']))
462 if steam_data
['personastate'] == 0 or steam_data
['personastate'] > 7:
463 # User is offline or unknown state
464 # NOTE: lastlogoff is actual logoff timestamp, not "appear offline" timestamp
465 latestdate
= get_timespan(datetime
.fromtimestamp(steam_data
['lastlogoff']))
466 message
+= u
""" @bLast seen@b {latestdate} ago @sep""".format(
467 latestdate
= latestdate
)
468 self
.msg(channel
, message
)
470 # user is online, busy, away, snooze, looking to trade or looking to play
471 if 'gameextrainfo' in steam_data
:
472 message
+= u
""" @bPlaying:@b {gamename} @sep""".format(
473 gamename
= steam_data
['gameextrainfo'])
474 if 'gameserverip' in steam_data
:
475 message
+= u
""" @bPlaying on server:@b {gameserver} @sep""".format(
476 gameserver
= steam_data
['gameserverip'])
478 # User is not playing a game.
479 message
+= u
""" Not playing anything right now @sep"""
481 self
.msg(channel
, message
)
483 def command_lastfm(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
485 user
= self
.lastfm
.get_user(arg
)
487 self
.errormsg(channel
, user
['message'])
489 latest
= self
.lastfm
.get_recent_tracks(arg
, 1)
490 except FeedError
as e
:
491 self
.errormsg(channel
, e
.msg
)
497 userinfo
.append(user
['realname'])
499 userinfo
.append(user
['age'])
501 userinfo
.append(user
['country'])
504 userinfo
= ' [%s]' % ', '.join(userinfo
)
508 if 'track' in latest
['recenttracks']:
509 if isinstance(latest
['recenttracks']['track'], list):
510 latest
= latest
['recenttracks']['track'][0]
512 latest
= latest
['recenttracks']['track']
514 latest
['@attr']['nowplaying']
515 latest_str
= u
' @bNow playing@b {latest[artist][#text]} - {latest[name]} @sep'.format(latest
=latest
)
517 latestdate
= get_timespan(datetime
.fromtimestamp(int(latest
['date']['uts'])))
518 latest_str
= u
' @bLatest track@b {latest[artist][#text]} - {latest[name]} ({latestdate} ago) @sep'.format(
519 latest
=latest
, latestdate
=latestdate
)
523 self
.msg(channel
, u
'@sep @b{user[name]}@b{userinfo} @sep @bPlays@b {plays} since {regdate} @sep \
524 @bLink@b {user[url]} @sep{latest_track}'.format(
526 plays
= format_thousand(int(user
['playcount'])),
527 regdate
= user
['registered']['#text'][:10],
529 latest_track
= latest_str
))
531 def command_url_shorten(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
532 if not arg
.startswith('http://') and not arg
.startswith('https://'):
533 self
.errormsg(channel
, 'a valid URL must start with http:// or https://')
537 reply
= self
.urls
.shorten(arg
)
539 self
.errormsg(channel
, e
.msg
)
542 if reply
['status_code'] != 200:
543 self
.errormsg(channel
, 'an error occurred.')
544 self
.elog
.warning('[shorten] error: code %d, %s' % (reply
['status_code'], reply
['status_txt']))
546 self
.msg(channel
, '@sep @bShort URL@b %s @sep' % reply
['data']['url'])
548 def command_url_expand(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
549 if not arg
.startswith('http://') and not arg
.startswith('https://'):
550 self
.errormsg(channel
, 'a valid URL must start with http:// or https://')
554 reply
= self
.urls
.expand(arg
)
556 self
.errormsg(channel
, e
.msg
)
560 self
.errormsg(channel
, reply
['error'])
562 self
.msg(channel
, '@sep @bLong URL@b {reply[long-url]} @sep @bContent-type@b {reply[content-type]} @sep'.format(reply
=reply
))
564 def command_idlerpg(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
566 player
= IrpgPlayer(arg
)
568 self
.errormsg(channel
, e
.msg
)
572 self
.errormsg(channel
, 'player not found. @bNote@b: nicks are case sensitive.')
575 self
.msg(channel
, """@sep @b{player.name}@b [{status}] @sep @bLevel@b {player.level} {player.classe} @sep @bNext level@b \
576 {nextlevel} @sep @bIdled@b {idled_for} @sep @bAlignment@b {player.alignment} @sep""".format(
578 status
= '@c3ON@c' if player
.is_online
else '@c4OFF@c',
579 nextlevel
= timedelta(seconds
=player
.ttl
),
580 idled_for
= timedelta(seconds
=player
.idled_for
)))
582 def command_ipinfo(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
584 reply
= self
.ipinfo
.get_info(arg
)
586 self
.errormsg(channel
, e
.msg
)
589 self
.msg(channel
, """@sep @bIP/Host@b {arg} ({reply[ip_addr]}) @sep @bLocation@b {reply[city]}, {reply[region]}, \
590 {reply[country_name]} [{reply[country_code]}] @sep{map}""".format(
593 map = ' http://maps.google.com/maps?q=%s,%s @sep' % (reply
['latitude'], reply
['longitude']) if reply
['latitude'] and reply
['longitude'] else ''))
595 dice_regex
= re
.compile('^(?:(\d+)d)?(\d+)(?:([\+\-])(\d+))?$')
597 def command_dice(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
598 r
= dice_regex
.search(arg
)
600 self
.errormsg(channel
, 'invalid format')
603 num
, faces
, type, modifier
= r
.groups()
609 if num
< 1 or num
> 32 or faces
< 2 or faces
> 65536:
610 self
.errormsg(channel
, 'parameter out of range')
615 for n
in xrange(int(num
)):
616 randnum
= random
.randint(1, int(faces
))
618 results
.append(randnum
)
621 modifier
= int(modifier
)
623 max = num
* faces
- modifier
625 modifier
= int(modifier
)
627 max = num
* faces
+ modifier
631 self
.msg(channel
, '@sep @bTotal@b {total} / {max} [{percent}%] @sep @bResults@b {results} @sep'.format(
634 percent
= 100 * total
/ max if max != 0 else '9001',
635 results
= str(results
)))
637 def command_qdb(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
640 quote
= self
.quotes
.get_qdb_random()
644 quote
= self
.quotes
.get_qdb_id(quote_id
)
646 self
.errormsg(channel
, 'quote @b%d@b not found' % quote_id
)
649 self
.errormsg(channel
, 'invalid quote ID')
651 except ExpatError
: # qdb returns a malformed xml when the quote doesn't exist
652 self
.errormsg(channel
, 'quote @b%d@b not found' % quote_id
)
655 self
.errormsg(channel
, e
.msg
)
659 for line
in quote
['lines']:
660 self
.msg(channel
, u
'[qdb {id}] {line}'.format(id=id, line
=line
.replace('\n', '')))
662 def command_fml(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
665 quote
= self
.quotes
.get_fml()
669 quote
= self
.quotes
.get_fml(quote_id
)
671 self
.errormsg(channel
, 'quote @b%d@b not found' % quote_id
)
673 except (ValueError, IndexError):
674 self
.errormsg(channel
, 'invalid quote ID')
676 except (FeedError
, FmlException
) as e
:
677 self
.errormsg(channel
, e
.msg
)
678 self
.elog
.warning('WARNING: .fml error: %s' % e
.msg
)
681 self
.msg(channel
, u
'[fml #{quote[id]}] {quote[text]}'.format(quote
=quote
))
683 def command_internets_info(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
684 self
.notice(sender
, '@sep @bRizon Internets Bot@b @sep @bDevelopers@b martin <martin@rizon.net> @sep @bHelp/feedback@b %(channel)s @sep' % {
685 'channel' : '#internets'})
687 def command_internets_help(self
, manager
, opts
, arg
, channel
, sender
, userinfo
):
688 command
= arg
.lower()
691 message
= ['internets: .help internets - for internets commands']
692 elif command
== 'internets':
693 message
= manager
.get_help()
695 message
= manager
.get_help(command
)
698 message
= ['%s is not a valid command.' % arg
]
701 self
.notice(sender
, line
)
703 class UserCommandManager(CommandManager
):
704 def get_prefix(self
):
707 def get_commands(self
):
710 'calc': (command_calc
, ARG_YES
, 'Calculates an expression', [], 'expression'),
712 'dict': 'dictionary',
713 'dictionary': (command_dictionary
, ARG_YES
, 'Search for a dictionary definition', [
714 ('number', '-n', 'display the n-th result', {'type': '+integer'}
, ARG_YES
),
715 ('all', '-a', 'display all results (using /notice)', {'action': 'store_true'}
, ARG_YES
)], 'word'),
717 'u': 'urbandictionary',
718 'urbandictionary': (command_urbandictionary
, ARG_YES
, 'Search for a definition on Urban Dictionary', [], 'word'),
721 'google': (command_google_search
, ARG_YES
, 'Search for something on Google', [], 'google_search'),
723 'gi': 'google_image',
724 'google_image': (command_google_image_search
, ARG_YES
, 'Search for images via Google Image', [], 'google_image_search'),
727 'translate': (command_bing_translate
, ARG_YES
, 'Translate something from a language to another', [], 'from to text'),
730 'youtube': (command_youtube_search
, ARG_YES
, 'Search for something on YouTube', [], 'youtube_search'),
733 'weather': (command_weather
, ARG_OPT
, 'Displays current weather conditions for a location', [
734 ('nick', '-n', 'use the weather location linked to a nick', {'action': 'store_true'}
, ARG_YES
)]),
737 'forecast': (command_forecast
, ARG_OPT
, 'Displays 5-day forecast for a location', [
738 ('nick', '-n', 'use the weather location linked to a nick', {'action': 'store_true'}
, ARG_YES
)]),
740 'regloc': 'register_location',
741 'register_location': (command_register_location
, ARG_YES
, 'Links a location to your nick that will be used as default location in .w and .f', [], 'location'),
743 'imdb': (command_imdb
, ARG_YES
, 'Search for information on a movie on IMDB', [], 'movie_title'),
745 'lastfm': (command_lastfm
, ARG_YES
, 'Returns information on a Last.fm user', [], 'lastfm_user'),
747 'shorten': (command_url_shorten
, ARG_YES
, 'Shortens a URL using http://j.mp', [], 'long_url'),
749 'expand': (command_url_expand
, ARG_YES
, 'Expands a shortened URL using http://longurl.org', [], 'shortened_url'),
752 'idlerpg': (command_idlerpg
, ARG_YES
, 'Returns info on a player in Rizon IdleRPG (http://idlerpg.rizon.net/)', [], 'player_name'),
754 'ipinfo': (command_ipinfo
, ARG_YES
, 'Returns short info on a IP address/hostname', [], 'ip/host'),
757 'dice': (command_dice
, ARG_YES
, 'Rolls X N-sided dice with an optional modifier A (XdN+A format)', [], 'dice_notation'),
759 'qdb': (command_qdb
, ARG_OPT
, 'Displays a quote from qdb.us', []),
761 'fml': (command_fml
, ARG_OPT
, 'Displays a quote from http://www.fmylife.com', []),
763 'steam': (command_steam
, ARG_OPT
, 'Shows your steam information', [
764 ('nick', '-n', 'use the steamid linked to a nick.', {'action': 'store_true'}
, ARG_YES
),
765 ('games', '-g', 'shows the total games owned by nick and shows most played game.', {'action': 'store_true'}
, ARG_NO
)]),
767 'regsteam': 'register_steam',
768 'register_steam': (command_register_steam
, ARG_YES
, 'Registers your Steam user ID', [], 'steamid'),
770 'info': (command_internets_info
, ARG_NO|ARG_OFFLINE
, 'Displays version and author information', []),
771 'help': (command_internets_help
, ARG_OPT|ARG_OFFLINE
, 'Displays available commands and their usage', []),