]> jfr.im git - irc/rizon/acid.git/blob - pyva/pyva/src/main/python/internets/internets.py
pyva/internets: YouTube link parsing
[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_auth, sys_channels, sys_log, sys_options, cmd_manager, inviteable, argparser
21
22 import cmd_admin, cmd_user, internets_users
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 self.bind_admin_commands()
82
83 def start_threads(self):
84 self.options.start()
85 self.channels.start()
86 self.users.start()
87 self.auth.start()
88 self.antiflood.start()
89
90 def start(self):
91 try:
92 AcidPlugin.start(self)
93 inviteable.InviteablePseudoclient.start(self)
94
95 self.options = sys_options.OptionManager(self)
96 self.elog = sys_log.LogManager(self)
97 self.commands_user = cmd_user.UserCommandManager()
98 except Exception, err:
99 self.log.exception('Error initializing core subsystems for internets module (%s)' % err)
100 raise
101
102 self.elog.debug('Started core subsystems.')
103
104 try:
105 self.channels = sys_channels.ChannelManager(self)
106 self.users = internets_users.InternetsUserManager(self)
107 self.auth = sys_auth.AuthManager(self)
108 self.antiflood = sys_antiflood.AntiFloodManager(self)
109 except Exception, err:
110 self.log.exception('Error initializing subsystems for internets module (%s)' % err)
111 raise
112
113 self.elog.debug('Started subsystems.')
114
115 try:
116 try:
117 self.bing = bing.Bing(self.config.get('internets').get('bing_appid'))
118 except Exception, err:
119 self.log.exception('Error initializing internets bing API (%s)' % err)
120 self.nsp = calc.NumericStringParser()
121 self.google = google.Google(self.config.get('internets').get('key_google'), self.config.get('internets').get('yt_parse_delay'))
122 self.imdb = imdb.Imdb()
123 self.ipinfo = ipinfo.IpInfo(self.config.get('internets').get('key_ipinfodb'))
124 self.lastfm = lastfm.LastFm(self.config.get('internets').get('key_lastfm'))
125 self.quotes = quotes.Quotes(self.config.get('internets').get('key_fml'))
126 self.urbandictionary = urbandictionary.UrbanDictionary()
127 self.urls = urls.Urls(self.config.get('internets').get('user_bitly'), self.config.get('internets').get('key_bitly'))
128 self.weather = weather.Weather(self.config.get('internets').get('key_openweathermap'))
129 self.wolfram = wolfram.Wolfram(self.config.get('internets').get('key_wolframalpha'))
130 self.wordnik = words.Words(self.config.get('internets').get('key_wordnik'))
131 self.steam = steam.Steam(self.config.get('internets').get('key_steam'))
132 self.twitch = twitch.Twitch()
133 except Exception, err:
134 self.log.exception('Error initializing internets module (%s)' % err)
135 raise
136
137 for channel in self.channels.list_valid():
138 self.join(channel.name)
139
140 self.log.debug('Joined channels.')
141
142 try:
143 self.start_threads()
144 except Exception, err:
145 self.log.exception('Error starting threads for internets module (%s)' % err)
146 raise
147
148 self.initialized = True
149 self.online = True
150 self.elog.debug('Started threads.')
151 return True
152
153 def stop(self):
154 if hasattr(self, 'antiflood'):
155 self.antiflood.stop()
156
157 if hasattr(self, 'auth'):
158 self.auth.stop()
159
160 if hasattr(self, 'users'):
161 if self.initialized:
162 self.users.force()
163
164 self.users.stop()
165 self.users.db_close()
166
167 if hasattr(self, 'channels'):
168 if self.initialized:
169 self.channels.force()
170
171 self.channels.stop()
172 self.channels.db_close()
173
174 if hasattr(self, 'options'):
175 if self.initialized:
176 self.options.force()
177
178 self.options.stop()
179 self.options.db_close()
180
181 def errormsg(self, target, message):
182 self.msg(target, '@b@c4Error:@o %s' % message)
183
184 def usagemsg(self, target, description, examples):
185 message = '@errsep @bUsage@b %s @errsep' % description
186
187 if examples != None:
188 message += ' @bExamples@b %s @errsep' % ', '.join(examples)
189
190 self.msg(target, message)
191
192 def msg(self, target, message):
193 if message != '':
194 Acidictive.privmsg(self.nick, target, format_ascii_irc(message))
195
196 def multimsg(self, target, count, intro, separator, pieces, outro = ''):
197 cur = 0
198
199 while cur < len(pieces):
200 self.msg(target, intro + separator.join(pieces[cur:cur + count]) + outro)
201 cur += count
202
203 def notice(self, target, message):
204 if message != '':
205 Acidictive.notice(self.nick, target, format_ascii_irc(message))
206
207 def execute(self, manager, command, argument, channel, sender, userinfo):
208 full_command = '%s%s' % (command, ' %s' % argument if len(argument) else '')
209 cmd = manager.get_command(command)
210
211 if cmd == None:
212 self.msg(channel, manager.invalid)
213 self.elog.debug('Parsed command @b%s@b: invalid command.' % full_command)
214 return
215
216 if self.users.is_banned(sender) or self.antiflood.check_user(sender, command, argument):
217 user = self.users[sender]
218 message = 'You were banned by @b%s@b.' % user.ban_source
219
220 if user.ban_reason != None:
221 message += ' Reason: @b%s@b.' % user.ban_reason
222
223 if user.ban_expiry != None:
224 message += ' Expires: @b%s@b.' % datetime.fromtimestamp(user.ban_expiry)
225
226 self.notice(sender, message)
227 self.elog.debug('Parsed command @b%s@b: user is banned.' % full_command)
228 return
229
230 self.elog.command('%s%s > %s' % (sender, ':%s' % channel if channel != sender else '', full_command))
231
232 parser = argparser.ArgumentParser(add_help_option = False, option_class = argparser.ArgumentParserOption)
233 cmd_type = cmd[1]
234 cmd_args = cmd[3]
235
236 parser.add_option('-?', '--help', action = 'store_true')
237
238 for cmd_arg in cmd_args:
239 parser.add_option(cmd_arg[1], '--' + cmd_arg[0], **cmd_arg[3])
240
241 try:
242 argument = argument.lstrip()
243 if len(argument) > 1:
244 if argument[0] == '-' and argument[1] != '-' and not cmd_args:
245 argument = '-- ' + argument
246 (popts, pargs) = parser.parse_args(args = argument.split(' '))
247 except argparser.ArgumentParserError, err:
248 self.msg(channel, str(err)) #TODO: Avoid str, use unicode.
249 parser.destroy()
250 self.elog.debug('Parsed command @b%s@b: invalid options.' % full_command)
251 return
252
253 if popts.help == True:
254 manager.commands['help'][0](self, manager, {}, command, channel, sender)
255 parser.destroy()
256 self.elog.debug('Parsed command @b%s@b: help intercepted.' % full_command)
257 return
258
259 opt_dict = {}
260 larg = ' '.join(pargs).strip()
261 is_offline = True
262
263 for cmd_arg in cmd_args:
264 parg = getattr(popts, cmd_arg[0])
265
266 if parg != None:
267 if len(cmd_arg) <= 4 or not (cmd_arg[4] & cmd_manager.ARG_OFFLINE):
268 is_offline = False
269
270 if len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_YES) and larg == '':
271 self.msg(channel, 'Error: %s option requires an argument.' % cmd_arg[1])
272 parser.destroy()
273 self.elog.debug('Parsed command @b%s@b: option constraint was broken.' % full_command)
274 return
275
276 opt_dict[cmd_arg[0]] = parg
277 elif len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_OFFLINE and cmd_arg[4] & cmd_manager.ARG_OFFLINE_REQ):
278 is_offline = False
279
280 if not self.online and ((len(pargs) > 0 and not (cmd_type & cmd_manager.ARG_OFFLINE)) or not is_offline):
281 self.notice(sender, 'The eRepublik API is offline. Please retry later.')
282 parser.destroy()
283 self.elog.debug('Parsed command @b%s@b: offline.' % full_command)
284 return
285
286 if (cmd_type & cmd_manager.ARG_YES) and (larg == None or larg == ''):
287 self.notice(sender, '@bUsage@b: %s @b%s@b' % (command, cmd[4] if len(cmd) > 4 else 'argument'))
288 else:
289 try:
290 cmd[0](self, manager, opt_dict, larg, channel, sender, userinfo)
291 except Exception, e:
292 tb = traceback.extract_tb(sys.exc_info()[2])
293 longest = 0
294
295 for entry in tb:
296 length = len(entry[2])
297
298 if length > longest:
299 longest = length
300
301 self.elog.exception('%s%s > @b%s@b: %s' % (sender, ':%s' % channel if channel != sender else '', full_command, e))
302 self.log.exception("internets error!")
303
304 for entry in tb:
305 self.elog.traceback('@b%-*s@b : %d %s' % (longest, entry[2], entry[1], entry[3]))
306
307 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.')
308
309 parser.destroy()
310 self.elog.debug('Parsed command @b%s@b: execution terminated.' % full_command)
311
312 def onPrivmsg(self, source, target, message):
313 if not super(internets, self).onPrivmsg(source, target, message):
314 # It's not a command
315 return
316
317 if not self.users.is_banned(User.findUser(source)['nick']) and target[0] == '#':
318 if 'youtu' in message:
319 # both 'youtube.com' and 'youtu.be' get caught
320 threading.deferToThread(cmd_user.onPrivmsg_regex_youtube, self, source, target, message)
321
322 def onChanModes(self, prefix, channel, modes):
323 if not self.initialized:
324 return
325
326 if not modes == '-z':
327 return
328
329 if channel in self.channels:
330 self.channels.remove(channel)
331 self.elog.request('Channel @b%s@b was dropped. Deleting it.' % channel)
332
333 def getCommands(self):
334 return self.commands_admin
335
336 def get_location(self, opts, arg, channel, sender):
337 nick = None
338 location = None
339
340 if 'nick' in opts:
341 nick = arg
342 elif arg == '':
343 nick = sender
344 else:
345 location = arg
346
347 if nick:
348 location = self.users.get(nick, 'location')
349
350 if not location:
351 if 'nick' in opts:
352 self.msg(channel, 'No location found linked to nick %s.' % arg)
353 else:
354 self.msg(channel, 'No location found linked to your nick. To link one, type: @b%sregister_location <location>@b' % self.commands_user.get_prefix())
355
356 return location
357
358 def get_steamid(self, opts, arg, channel, sender):
359 """Gets the steamid from the database."""
360 nick = None
361 steamid = None
362
363 if 'nick' in opts:
364 nick = arg
365 else:
366 nick = sender
367
368 steamid = self.users.get(nick, 'steamid')
369
370 if not steamid:
371 if 'nick' in opts:
372 self.msg(channel, 'No steamid found linked to nick %s.' % arg)
373 return
374 else:
375 self.msg(channel, 'No steamid found linked to your nick. To link one, type: @b%sregister_steam <steamid>@b' % self.commands_user.get_prefix())
376 return
377 else:
378 return SteamUser(nick, steamid)