]> jfr.im git - irc/rizon/acid.git/blame - pyva/pyva/src/main/python/internets/internets.py
Add the ability to supply an api key
[irc/rizon/acid.git] / pyva / pyva / src / main / python / internets / internets.py
CommitLineData
685e346e
A
1#!/usr/bin/python pseudoserver.py
2# psm_internets.py
3# module for pypseudoserver
4# written by ElChE <elche@rizon.net>, martin <martin@rizon.net>
5
6import sys
7import traceback
8import types
9import logging
10
11from istring import istring
12from datetime import datetime
13from decimal import Decimal, InvalidOperation
14
15from utils import *
16from pyva import *
17from core import *
18from plugin import *
19import mythreading as threading
02901647 20from pseudoclient import sys_antiflood, sys_log, sys_options, cmd_manager, inviteable, argparser
685e346e 21
02901647 22import cmd_admin, cmd_private, cmd_user, internets_channels, internets_users, sys_auth
f0ac597d 23from api import bing, calc, google, imdb, ipinfo, lastfm, quotes, urbandictionary, urls, weather, wolfram, words, steam, twitch
685e346e 24from internets_utils import *
9c9f0247 25from api.steam import SteamUser
685e346e 26
2d09c59a 27import pyva_net_rizon_acid_core_Acidictive as Acidictive
f6353b7a 28import pyva_net_rizon_acid_core_AcidCore as AcidCore
2d09c59a 29import pyva_net_rizon_acid_core_User as User
f6353b7a 30
685e346e
A
31class internets(
32 AcidPlugin,
33 inviteable.InviteablePseudoclient
34):
35 initialized = False
36
37 def bind_function(self, function):
38 func = types.MethodType(function, self, internets)
39 setattr(internets, function.__name__, func)
40 return func
41
42 def bind_admin_commands(self):
43 list = cmd_admin.get_commands()
44 self.commands_admin = []
45
46 for command in list:
47 self.commands_admin.append(
48 (command,
49 {
50 'permission': 'e',
51 'callback': self.bind_function(list[command][0]),
52 'usage': list[command][1]
53 }
54 )
55 )
56
57 def __init__(self):
58 AcidPlugin.__init__(self)
59
60 self.name = "internets"
61 self.log = logging.getLogger(__name__)
62
63 try:
41ae6aae 64 self.nick = istring(self.config.get('internets').get('nick'))
685e346e
A
65 except Exception, err:
66 self.log.exception("Error reading 'internets:nick' configuration option: %s" % err)
67 raise
68
69 try:
41ae6aae 70 self.chan = istring(self.config.get('internets').get('channel'))
685e346e
A
71 except Exception, err:
72 self.log.exception("Error reading 'internets:channel' configuration option: %s" % err)
73 raise
74
d0baaf8a
D
75 try:
76 self.output_limit = int(self.config.get('internets').get('outputlimit'))
77 except Exception, err:
78 self.log.exception("Error reading 'internets:outputlimit' configuration option: %s" % err)
79 raise
80
efd2c73c
O
81 try:
82 self.max_line_length = int(self.config.get('internets').get('maxlinelength'))
83 except Exception, err:
84 self.log.exception("Error reading 'internets:maxlinelength' configuration option: %s" % err)
85 raise
86
685e346e
A
87 self.bind_admin_commands()
88
89 def start_threads(self):
90 self.options.start()
91 self.channels.start()
92 self.users.start()
93 self.auth.start()
94 self.antiflood.start()
95
96 def start(self):
97 try:
98 AcidPlugin.start(self)
99 inviteable.InviteablePseudoclient.start(self)
100
101 self.options = sys_options.OptionManager(self)
102 self.elog = sys_log.LogManager(self)
acd76da0 103 self.commands_private = cmd_private.PrivateCommandManager()
685e346e
A
104 self.commands_user = cmd_user.UserCommandManager()
105 except Exception, err:
106 self.log.exception('Error initializing core subsystems for internets module (%s)' % err)
107 raise
108
109 self.elog.debug('Started core subsystems.')
110
111 try:
acd76da0 112 self.channels = internets_channels.InternetsChannelManager(self)
685e346e 113 self.users = internets_users.InternetsUserManager(self)
acd76da0 114 self.auth = sys_auth.InternetsAuthManager(self)
685e346e
A
115 self.antiflood = sys_antiflood.AntiFloodManager(self)
116 except Exception, err:
117 self.log.exception('Error initializing subsystems for internets module (%s)' % err)
118 raise
119
120 self.elog.debug('Started subsystems.')
121
122 try:
123 try:
41ae6aae 124 self.bing = bing.Bing(self.config.get('internets').get('bing_appid'))
685e346e
A
125 except Exception, err:
126 self.log.exception('Error initializing internets bing API (%s)' % err)
127 self.nsp = calc.NumericStringParser()
adadb95c 128 self.google = google.Google(self.config.get('internets').get('key_google'), self.config.get('internets').get('yt_parse_delay'))
42d82f07 129 self.imdb = imdb.Imdb(self.config.get('internets').get('key_imdb'))
41ae6aae 130 self.ipinfo = ipinfo.IpInfo(self.config.get('internets').get('key_ipinfodb'))
131 self.lastfm = lastfm.LastFm(self.config.get('internets').get('key_lastfm'))
132 self.quotes = quotes.Quotes(self.config.get('internets').get('key_fml'))
685e346e 133 self.urbandictionary = urbandictionary.UrbanDictionary()
41ae6aae 134 self.urls = urls.Urls(self.config.get('internets').get('user_bitly'), self.config.get('internets').get('key_bitly'))
135 self.weather = weather.Weather(self.config.get('internets').get('key_openweathermap'))
136 self.wolfram = wolfram.Wolfram(self.config.get('internets').get('key_wolframalpha'))
137 self.wordnik = words.Words(self.config.get('internets').get('key_wordnik'))
138 self.steam = steam.Steam(self.config.get('internets').get('key_steam'))
f0ac597d 139 self.twitch = twitch.Twitch()
685e346e
A
140 except Exception, err:
141 self.log.exception('Error initializing internets module (%s)' % err)
142 raise
143
e7697283
A
144 for channel in self.channels.list_valid():
145 self.join(channel.name)
685e346e 146
e7697283 147 self.log.debug('Joined channels.')
685e346e
A
148
149 try:
150 self.start_threads()
151 except Exception, err:
152 self.log.exception('Error starting threads for internets module (%s)' % err)
153 raise
154
155 self.initialized = True
156 self.online = True
157 self.elog.debug('Started threads.')
158 return True
159
685e346e
A
160 def stop(self):
161 if hasattr(self, 'antiflood'):
162 self.antiflood.stop()
163
164 if hasattr(self, 'auth'):
165 self.auth.stop()
166
167 if hasattr(self, 'users'):
168 if self.initialized:
169 self.users.force()
170
171 self.users.stop()
172 self.users.db_close()
173
174 if hasattr(self, 'channels'):
175 if self.initialized:
176 self.channels.force()
177
178 self.channels.stop()
179 self.channels.db_close()
180
181 if hasattr(self, 'options'):
182 if self.initialized:
183 self.options.force()
184
185 self.options.stop()
186 self.options.db_close()
187
685e346e
A
188 def errormsg(self, target, message):
189 self.msg(target, '@b@c4Error:@o %s' % message)
190
191 def usagemsg(self, target, description, examples):
192 message = '@errsep @bUsage@b %s @errsep' % description
193
194 if examples != None:
195 message += ' @bExamples@b %s @errsep' % ', '.join(examples)
196
197 self.msg(target, message)
198
199 def msg(self, target, message):
200 if message != '':
2d09c59a 201 Acidictive.privmsg(self.nick, target, format_ascii_irc(message))
685e346e
A
202
203 def multimsg(self, target, count, intro, separator, pieces, outro = ''):
204 cur = 0
205
206 while cur < len(pieces):
207 self.msg(target, intro + separator.join(pieces[cur:cur + count]) + outro)
208 cur += count
209
210 def notice(self, target, message):
211 if message != '':
2d09c59a 212 Acidictive.notice(self.nick, target, format_ascii_irc(message))
685e346e
A
213
214 def execute(self, manager, command, argument, channel, sender, userinfo):
215 full_command = '%s%s' % (command, ' %s' % argument if len(argument) else '')
216 cmd = manager.get_command(command)
217
218 if cmd == None:
219 self.msg(channel, manager.invalid)
220 self.elog.debug('Parsed command @b%s@b: invalid command.' % full_command)
221 return
222
223 if self.users.is_banned(sender) or self.antiflood.check_user(sender, command, argument):
224 user = self.users[sender]
225 message = 'You were banned by @b%s@b.' % user.ban_source
226
227 if user.ban_reason != None:
228 message += ' Reason: @b%s@b.' % user.ban_reason
229
230 if user.ban_expiry != None:
231 message += ' Expires: @b%s@b.' % datetime.fromtimestamp(user.ban_expiry)
232
233 self.notice(sender, message)
234 self.elog.debug('Parsed command @b%s@b: user is banned.' % full_command)
235 return
236
237 self.elog.command('%s%s > %s' % (sender, ':%s' % channel if channel != sender else '', full_command))
238
bdf4ecea 239 parser = argparser.ArgumentParser(add_help_option = False, option_class = argparser.ArgumentParserOption)
685e346e
A
240 cmd_type = cmd[1]
241 cmd_args = cmd[3]
242
243 parser.add_option('-?', '--help', action = 'store_true')
244
245 for cmd_arg in cmd_args:
246 parser.add_option(cmd_arg[1], '--' + cmd_arg[0], **cmd_arg[3])
247
248 try:
c2ee98e6 249 argument = argument.lstrip()
250 if len(argument) > 1:
251 if argument[0] == '-' and argument[1] != '-' and not cmd_args:
252 argument = '-- ' + argument
685e346e 253 (popts, pargs) = parser.parse_args(args = argument.split(' '))
bdf4ecea 254 except argparser.ArgumentParserError, err:
685e346e
A
255 self.msg(channel, str(err)) #TODO: Avoid str, use unicode.
256 parser.destroy()
257 self.elog.debug('Parsed command @b%s@b: invalid options.' % full_command)
258 return
259
260 if popts.help == True:
261 manager.commands['help'][0](self, manager, {}, command, channel, sender)
262 parser.destroy()
263 self.elog.debug('Parsed command @b%s@b: help intercepted.' % full_command)
264 return
265
266 opt_dict = {}
267 larg = ' '.join(pargs).strip()
268 is_offline = True
269
270 for cmd_arg in cmd_args:
271 parg = getattr(popts, cmd_arg[0])
272
273 if parg != None:
274 if len(cmd_arg) <= 4 or not (cmd_arg[4] & cmd_manager.ARG_OFFLINE):
275 is_offline = False
276
277 if len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_YES) and larg == '':
278 self.msg(channel, 'Error: %s option requires an argument.' % cmd_arg[1])
279 parser.destroy()
280 self.elog.debug('Parsed command @b%s@b: option constraint was broken.' % full_command)
281 return
282
283 opt_dict[cmd_arg[0]] = parg
284 elif len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_OFFLINE and cmd_arg[4] & cmd_manager.ARG_OFFLINE_REQ):
285 is_offline = False
286
287 if not self.online and ((len(pargs) > 0 and not (cmd_type & cmd_manager.ARG_OFFLINE)) or not is_offline):
288 self.notice(sender, 'The eRepublik API is offline. Please retry later.')
289 parser.destroy()
290 self.elog.debug('Parsed command @b%s@b: offline.' % full_command)
291 return
292
293 if (cmd_type & cmd_manager.ARG_YES) and (larg == None or larg == ''):
294 self.notice(sender, '@bUsage@b: %s @b%s@b' % (command, cmd[4] if len(cmd) > 4 else 'argument'))
295 else:
296 try:
297 cmd[0](self, manager, opt_dict, larg, channel, sender, userinfo)
298 except Exception, e:
299 tb = traceback.extract_tb(sys.exc_info()[2])
300 longest = 0
301
302 for entry in tb:
303 length = len(entry[2])
304
305 if length > longest:
306 longest = length
307
308 self.elog.exception('%s%s > @b%s@b: %s' % (sender, ':%s' % channel if channel != sender else '', full_command, e))
309 self.log.exception("internets error!")
310
311 for entry in tb:
312 self.elog.traceback('@b%-*s@b : %d %s' % (longest, entry[2], entry[1], entry[3]))
313
314 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.')
315
316 parser.destroy()
317 self.elog.debug('Parsed command @b%s@b: execution terminated.' % full_command)
318
adadb95c
M
319 def onPrivmsg(self, source, target, message):
320 if not super(internets, self).onPrivmsg(source, target, message):
321 # It's not a command
322 return
323
6f5b3072
O
324 if not target in self.channels:
325 return
326
0b1c92a7 327 # both 'youtube.com' and 'youtu.be' get caught
eadfde5a 328 if not 'youtu' in message or not self.channels[target].youtube_info:
0b1c92a7
A
329 return
330
331 sourceNick = User.findUser(source)['nick']
332 if not self.users.is_banned(sourceNick) and not self.antiflood.check_user(sourceNick, 'ytinfo', '') and target[0] == '#':
333 threading.deferToThread(cmd_user.onPrivmsg_regex_youtube, self, source, target, message)
adadb95c 334
685e346e
A
335 def onChanModes(self, prefix, channel, modes):
336 if not self.initialized:
337 return
338
339 if not modes == '-z':
340 return
341
342 if channel in self.channels:
343 self.channels.remove(channel)
344 self.elog.request('Channel @b%s@b was dropped. Deleting it.' % channel)
345
346 def getCommands(self):
347 return self.commands_admin
348
349 def get_location(self, opts, arg, channel, sender):
350 nick = None
351 location = None
352
353 if 'nick' in opts:
354 nick = arg
355 elif arg == '':
356 nick = sender
357 else:
358 location = arg
359
360 if nick:
361 location = self.users.get(nick, 'location')
362
363 if not location:
364 if 'nick' in opts:
365 self.msg(channel, 'No location found linked to nick %s.' % arg)
366 else:
9c9f0247 367 self.msg(channel, 'No location found linked to your nick. To link one, type: @b%sregister_location <location>@b' % self.commands_user.get_prefix())
685e346e
A
368
369 return location
9c9f0247
M
370
371 def get_steamid(self, opts, arg, channel, sender):
372 """Gets the steamid from the database."""
373 nick = None
374 steamid = None
375
376 if 'nick' in opts:
377 nick = arg
378 else:
379 nick = sender
380
381 steamid = self.users.get(nick, 'steamid')
382
383 if not steamid:
384 if 'nick' in opts:
385 self.msg(channel, 'No steamid found linked to nick %s.' % arg)
386 return
387 else:
388 self.msg(channel, 'No steamid found linked to your nick. To link one, type: @b%sregister_steam <steamid>@b' % self.commands_user.get_prefix())
389 return
390 else:
391 return SteamUser(nick, steamid)