]> jfr.im git - irc/rizon/acid.git/blame - pyva/pyva/src/main/python/internets/internets.py
Truncate string instead of sending complete msg
[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 != '':
72453076 201 if len(message) > 450: # from Protocol.privmsg -> long, do not spam chan
7e002c01
D
202 message = message[:445] + ' ...'
203 Acidictive.privmsg(self.nick, target, format_ascii_irc(message))
685e346e
A
204
205 def multimsg(self, target, count, intro, separator, pieces, outro = ''):
206 cur = 0
207
208 while cur < len(pieces):
209 self.msg(target, intro + separator.join(pieces[cur:cur + count]) + outro)
210 cur += count
211
212 def notice(self, target, message):
213 if message != '':
2d09c59a 214 Acidictive.notice(self.nick, target, format_ascii_irc(message))
685e346e
A
215
216 def execute(self, manager, command, argument, channel, sender, userinfo):
217 full_command = '%s%s' % (command, ' %s' % argument if len(argument) else '')
218 cmd = manager.get_command(command)
219
220 if cmd == None:
221 self.msg(channel, manager.invalid)
222 self.elog.debug('Parsed command @b%s@b: invalid command.' % full_command)
223 return
224
225 if self.users.is_banned(sender) or self.antiflood.check_user(sender, command, argument):
226 user = self.users[sender]
227 message = 'You were banned by @b%s@b.' % user.ban_source
228
229 if user.ban_reason != None:
230 message += ' Reason: @b%s@b.' % user.ban_reason
231
232 if user.ban_expiry != None:
233 message += ' Expires: @b%s@b.' % datetime.fromtimestamp(user.ban_expiry)
234
235 self.notice(sender, message)
236 self.elog.debug('Parsed command @b%s@b: user is banned.' % full_command)
237 return
238
239 self.elog.command('%s%s > %s' % (sender, ':%s' % channel if channel != sender else '', full_command))
240
bdf4ecea 241 parser = argparser.ArgumentParser(add_help_option = False, option_class = argparser.ArgumentParserOption)
685e346e
A
242 cmd_type = cmd[1]
243 cmd_args = cmd[3]
244
245 parser.add_option('-?', '--help', action = 'store_true')
246
247 for cmd_arg in cmd_args:
248 parser.add_option(cmd_arg[1], '--' + cmd_arg[0], **cmd_arg[3])
249
250 try:
c2ee98e6 251 argument = argument.lstrip()
252 if len(argument) > 1:
253 if argument[0] == '-' and argument[1] != '-' and not cmd_args:
254 argument = '-- ' + argument
685e346e 255 (popts, pargs) = parser.parse_args(args = argument.split(' '))
bdf4ecea 256 except argparser.ArgumentParserError, err:
685e346e
A
257 self.msg(channel, str(err)) #TODO: Avoid str, use unicode.
258 parser.destroy()
259 self.elog.debug('Parsed command @b%s@b: invalid options.' % full_command)
260 return
261
262 if popts.help == True:
263 manager.commands['help'][0](self, manager, {}, command, channel, sender)
264 parser.destroy()
265 self.elog.debug('Parsed command @b%s@b: help intercepted.' % full_command)
266 return
267
268 opt_dict = {}
269 larg = ' '.join(pargs).strip()
270 is_offline = True
271
272 for cmd_arg in cmd_args:
273 parg = getattr(popts, cmd_arg[0])
274
275 if parg != None:
276 if len(cmd_arg) <= 4 or not (cmd_arg[4] & cmd_manager.ARG_OFFLINE):
277 is_offline = False
278
279 if len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_YES) and larg == '':
280 self.msg(channel, 'Error: %s option requires an argument.' % cmd_arg[1])
281 parser.destroy()
282 self.elog.debug('Parsed command @b%s@b: option constraint was broken.' % full_command)
283 return
284
285 opt_dict[cmd_arg[0]] = parg
286 elif len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_OFFLINE and cmd_arg[4] & cmd_manager.ARG_OFFLINE_REQ):
287 is_offline = False
288
289 if not self.online and ((len(pargs) > 0 and not (cmd_type & cmd_manager.ARG_OFFLINE)) or not is_offline):
290 self.notice(sender, 'The eRepublik API is offline. Please retry later.')
291 parser.destroy()
292 self.elog.debug('Parsed command @b%s@b: offline.' % full_command)
293 return
294
295 if (cmd_type & cmd_manager.ARG_YES) and (larg == None or larg == ''):
296 self.notice(sender, '@bUsage@b: %s @b%s@b' % (command, cmd[4] if len(cmd) > 4 else 'argument'))
297 else:
298 try:
299 cmd[0](self, manager, opt_dict, larg, channel, sender, userinfo)
300 except Exception, e:
301 tb = traceback.extract_tb(sys.exc_info()[2])
302 longest = 0
303
304 for entry in tb:
305 length = len(entry[2])
306
307 if length > longest:
308 longest = length
309
310 self.elog.exception('%s%s > @b%s@b: %s' % (sender, ':%s' % channel if channel != sender else '', full_command, e))
311 self.log.exception("internets error!")
312
313 for entry in tb:
314 self.elog.traceback('@b%-*s@b : %d %s' % (longest, entry[2], entry[1], entry[3]))
315
316 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.')
317
318 parser.destroy()
319 self.elog.debug('Parsed command @b%s@b: execution terminated.' % full_command)
320
adadb95c
M
321 def onPrivmsg(self, source, target, message):
322 if not super(internets, self).onPrivmsg(source, target, message):
323 # It's not a command
324 return
325
6f5b3072
O
326 if not target in self.channels:
327 return
328
0b1c92a7 329 # both 'youtube.com' and 'youtu.be' get caught
eadfde5a 330 if not 'youtu' in message or not self.channels[target].youtube_info:
0b1c92a7
A
331 return
332
333 sourceNick = User.findUser(source)['nick']
334 if not self.users.is_banned(sourceNick) and not self.antiflood.check_user(sourceNick, 'ytinfo', '') and target[0] == '#':
335 threading.deferToThread(cmd_user.onPrivmsg_regex_youtube, self, source, target, message)
adadb95c 336
685e346e
A
337 def onChanModes(self, prefix, channel, modes):
338 if not self.initialized:
339 return
340
341 if not modes == '-z':
342 return
343
344 if channel in self.channels:
345 self.channels.remove(channel)
346 self.elog.request('Channel @b%s@b was dropped. Deleting it.' % channel)
347
348 def getCommands(self):
349 return self.commands_admin
350
351 def get_location(self, opts, arg, channel, sender):
352 nick = None
353 location = None
354
355 if 'nick' in opts:
356 nick = arg
357 elif arg == '':
358 nick = sender
359 else:
360 location = arg
361
362 if nick:
363 location = self.users.get(nick, 'location')
364
365 if not location:
366 if 'nick' in opts:
367 self.msg(channel, 'No location found linked to nick %s.' % arg)
368 else:
9c9f0247 369 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
370
371 return location
9c9f0247
M
372
373 def get_steamid(self, opts, arg, channel, sender):
374 """Gets the steamid from the database."""
375 nick = None
376 steamid = None
377
378 if 'nick' in opts:
379 nick = arg
380 else:
381 nick = sender
382
383 steamid = self.users.get(nick, 'steamid')
384
385 if not steamid:
386 if 'nick' in opts:
387 self.msg(channel, 'No steamid found linked to nick %s.' % arg)
388 return
389 else:
390 self.msg(channel, 'No steamid found linked to your nick. To link one, type: @b%sregister_steam <steamid>@b' % self.commands_user.get_prefix())
391 return
392 else:
393 return SteamUser(nick, steamid)