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