]> jfr.im git - irc/rizon/acid.git/blob - pyva/pyva/src/main/python/internets/internets.py
38c0eb0a53fcf72255b9f2ebea194085eb8d95ef
[irc/rizon/acid.git] / pyva / pyva / src / main / python / internets / internets.py
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
6 import sys
7 import traceback
8 import types
9 import logging
10
11 from istring import istring
12 from datetime import datetime
13 from decimal import Decimal, InvalidOperation
14
15 from utils import *
16 from pyva import *
17 from core import *
18 from plugin import *
19 import mythreading as threading
20 from pseudoclient import sys_antiflood, sys_log, sys_options, cmd_manager, inviteable, argparser
21
22 import cmd_admin, cmd_private, cmd_user, internets_channels, internets_users, sys_auth
23 from api import bing, calc, google, imdb, ipinfo, lastfm, quotes, urbandictionary, urls, weather, wolfram, words, steam, twitch
24 from internets_utils import *
25 from api.steam import SteamUser
26
27 import pyva_net_rizon_acid_core_Acidictive as Acidictive
28 import pyva_net_rizon_acid_core_AcidCore as AcidCore
29 import pyva_net_rizon_acid_core_User as User
30
31 class 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:
64 self.nick = istring(self.config.get('internets').get('nick'))
65 except Exception, err:
66 self.log.exception("Error reading 'internets:nick' configuration option: %s" % err)
67 raise
68
69 try:
70 self.chan = istring(self.config.get('internets').get('channel'))
71 except Exception, err:
72 self.log.exception("Error reading 'internets:channel' configuration option: %s" % err)
73 raise
74
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
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
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)
103 self.commands_private = cmd_private.PrivateCommandManager()
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:
112 self.channels = internets_channels.InternetsChannelManager(self)
113 self.users = internets_users.InternetsUserManager(self)
114 self.auth = sys_auth.InternetsAuthManager(self)
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:
124 self.bing = bing.Bing(self.config.get('internets').get('bing_appid'))
125 except Exception, err:
126 self.log.exception('Error initializing internets bing API (%s)' % err)
127 self.nsp = calc.NumericStringParser()
128 self.google = google.Google(self.config.get('internets').get('key_google'), self.config.get('internets').get('yt_parse_delay'))
129 self.imdb = imdb.Imdb(self.config.get('internets').get('key_imdb'))
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'))
133 self.urbandictionary = urbandictionary.UrbanDictionary()
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'))
139 self.twitch = twitch.Twitch()
140 except Exception, err:
141 self.log.exception('Error initializing internets module (%s)' % err)
142 raise
143
144 for channel in self.channels.list_valid():
145 self.join(channel.name)
146
147 self.log.debug('Joined channels.')
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
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
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 != '':
201 Acidictive.privmsg(self.nick, target, format_ascii_irc(message))
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 != '':
212 Acidictive.notice(self.nick, target, format_ascii_irc(message))
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
239 parser = argparser.ArgumentParser(add_help_option = False, option_class = argparser.ArgumentParserOption)
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:
249 argument = argument.lstrip()
250 if len(argument) > 1:
251 if argument[0] == '-' and argument[1] != '-' and not cmd_args:
252 argument = '-- ' + argument
253 (popts, pargs) = parser.parse_args(args = argument.split(' '))
254 except argparser.ArgumentParserError, err:
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
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
324 if not target in self.channels:
325 return
326
327 # both 'youtube.com' and 'youtu.be' get caught
328 if not 'youtu' in message or not self.channels[target].youtube_info:
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)
334
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:
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())
368
369 return location
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)