]> jfr.im git - irc/rizon/acid.git/blob - pyva/src/main/python/internets/internets.py
.gitignore: Ignore all pyva logs
[irc/rizon/acid.git] / 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
21
22 import cmd_admin, cmd_user, erepparser, internets_users
23 from api import bing, calc, google, imdb, ipinfo, lastfm, quotes, urbandictionary, urls, weather, wolfram, words
24 from internets_utils import *
25
26 class internets(
27 AcidPlugin,
28 inviteable.InviteablePseudoclient
29 ):
30 initialized = False
31
32 def bind_function(self, function):
33 func = types.MethodType(function, self, internets)
34 setattr(internets, function.__name__, func)
35 return func
36
37 def bind_admin_commands(self):
38 list = cmd_admin.get_commands()
39 self.commands_admin = []
40
41 for command in list:
42 self.commands_admin.append(
43 (command,
44 {
45 'permission': 'e',
46 'callback': self.bind_function(list[command][0]),
47 'usage': list[command][1]
48 }
49 )
50 )
51
52 def __init__(self):
53 AcidPlugin.__init__(self)
54
55 self.name = "internets"
56 self.log = logging.getLogger(__name__)
57
58 try:
59 self.nick = istring(self.config.get('internets', 'nick'))
60 except Exception, err:
61 self.log.exception("Error reading 'internets:nick' configuration option: %s" % err)
62 raise
63
64 try:
65 self.chan = istring(self.config.get('internets', 'channel'))
66 except Exception, err:
67 self.log.exception("Error reading 'internets:channel' configuration option: %s" % err)
68 raise
69
70 self.bind_admin_commands()
71
72 def start_threads(self):
73 self.options.start()
74 self.channels.start()
75 self.users.start()
76 self.auth.start()
77 self.antiflood.start()
78
79 def start(self):
80 try:
81 AcidPlugin.start(self)
82 inviteable.InviteablePseudoclient.start(self)
83
84 self.options = sys_options.OptionManager(self)
85 self.elog = sys_log.LogManager(self)
86 self.commands_user = cmd_user.UserCommandManager()
87 except Exception, err:
88 self.log.exception('Error initializing core subsystems for internets module (%s)' % err)
89 raise
90
91 self.elog.debug('Started core subsystems.')
92
93 try:
94 self.channels = sys_channels.ChannelManager(self)
95 self.users = internets_users.InternetsUserManager(self)
96 self.auth = sys_auth.AuthManager(self)
97 self.antiflood = sys_antiflood.AntiFloodManager(self)
98 except Exception, err:
99 self.log.exception('Error initializing subsystems for internets module (%s)' % err)
100 raise
101
102 self.elog.debug('Started subsystems.')
103
104 try:
105 try:
106 self.bing = bing.Bing(self.config.get('internets', 'bing_appid'))
107 except Exception, err:
108 self.log.exception('Error initializing internets bing API (%s)' % err)
109 self.nsp = calc.NumericStringParser()
110 self.google = google.Google()
111 self.imdb = imdb.Imdb()
112 self.ipinfo = ipinfo.IpInfo(self.config.get('internets', 'key_ipinfodb'))
113 self.lastfm = lastfm.LastFm(self.config.get('internets', 'key_lastfm'))
114 self.quotes = quotes.Quotes(self.config.get('internets', 'key_fml'))
115 self.urbandictionary = urbandictionary.UrbanDictionary()
116 self.urls = urls.Urls(self.config.get('internets', 'user_bitly'), self.config.get('internets', 'key_bitly'))
117 self.weather = weather.Weather(self.config.get('internets', 'key_openweathermap'))
118 self.wolfram = wolfram.Wolfram(self.config.get('internets', 'key_wolframalpha'))
119 self.wordnik = words.Words(self.config.get('internets', 'key_wordnik'))
120 except Exception, err:
121 self.log.exception('Error initializing internets module (%s)' % err)
122 raise
123
124 for channel in self.channels.list_valid():
125 self.join(channel.name)
126
127 self.log.debug('Joined channels.')
128
129 try:
130 self.start_threads()
131 except Exception, err:
132 self.log.exception('Error starting threads for internets module (%s)' % err)
133 raise
134
135 self.initialized = True
136 self.online = True
137 self.elog.debug('Started threads.')
138 return True
139
140 def onSync(self):
141 for channel in self.channels.list_valid():
142 self.join(channel.name)
143
144 self.log.debug('Joined channels.')
145
146 def stop(self):
147 if hasattr(self, 'antiflood'):
148 self.antiflood.stop()
149
150 if hasattr(self, 'auth'):
151 self.auth.stop()
152
153 if hasattr(self, 'users'):
154 if self.initialized:
155 self.users.force()
156
157 self.users.stop()
158 self.users.db_close()
159
160 if hasattr(self, 'channels'):
161 if self.initialized:
162 self.channels.force()
163
164 self.channels.stop()
165 self.channels.db_close()
166
167 if hasattr(self, 'options'):
168 if self.initialized:
169 self.options.force()
170
171 self.options.stop()
172 self.options.db_close()
173
174 def join(self, channel):
175 me = self.inter.findUser(self.nick)
176 me.joinChan(channel)
177
178 def part(self, channel):
179 me = self.inter.findUser(self.nick)
180 me.partChan(channel)
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 self.inter.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 self.inter.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 = erepparser.ErepublikParser(add_help_option = False, option_class = erepparser.ErepublikParserOption)
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 (popts, pargs) = parser.parse_args(args = argument.split(' '))
244 except erepparser.ErepublikParserError, err:
245 self.msg(channel, str(err)) #TODO: Avoid str, use unicode.
246 parser.destroy()
247 self.elog.debug('Parsed command @b%s@b: invalid options.' % full_command)
248 return
249
250 if popts.help == True:
251 manager.commands['help'][0](self, manager, {}, command, channel, sender)
252 parser.destroy()
253 self.elog.debug('Parsed command @b%s@b: help intercepted.' % full_command)
254 return
255
256 opt_dict = {}
257 larg = ' '.join(pargs).strip()
258 is_offline = True
259
260 for cmd_arg in cmd_args:
261 parg = getattr(popts, cmd_arg[0])
262
263 if parg != None:
264 if len(cmd_arg) <= 4 or not (cmd_arg[4] & cmd_manager.ARG_OFFLINE):
265 is_offline = False
266
267 if len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_YES) and larg == '':
268 self.msg(channel, 'Error: %s option requires an argument.' % cmd_arg[1])
269 parser.destroy()
270 self.elog.debug('Parsed command @b%s@b: option constraint was broken.' % full_command)
271 return
272
273 opt_dict[cmd_arg[0]] = parg
274 elif len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_OFFLINE and cmd_arg[4] & cmd_manager.ARG_OFFLINE_REQ):
275 is_offline = False
276
277 if not self.online and ((len(pargs) > 0 and not (cmd_type & cmd_manager.ARG_OFFLINE)) or not is_offline):
278 self.notice(sender, 'The eRepublik API is offline. Please retry later.')
279 parser.destroy()
280 self.elog.debug('Parsed command @b%s@b: offline.' % full_command)
281 return
282
283 if (cmd_type & cmd_manager.ARG_YES) and (larg == None or larg == ''):
284 self.notice(sender, '@bUsage@b: %s @b%s@b' % (command, cmd[4] if len(cmd) > 4 else 'argument'))
285 else:
286 try:
287 cmd[0](self, manager, opt_dict, larg, channel, sender, userinfo)
288 except Exception, e:
289 tb = traceback.extract_tb(sys.exc_info()[2])
290 longest = 0
291
292 for entry in tb:
293 length = len(entry[2])
294
295 if length > longest:
296 longest = length
297
298 self.elog.exception('%s%s > @b%s@b: %s' % (sender, ':%s' % channel if channel != sender else '', full_command, e))
299 self.log.exception("internets error!")
300
301 for entry in tb:
302 self.elog.traceback('@b%-*s@b : %d %s' % (longest, entry[2], entry[1], entry[3]))
303
304 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.')
305
306 parser.destroy()
307 self.elog.debug('Parsed command @b%s@b: execution terminated.' % full_command)
308
309 def onChanModes(self, prefix, channel, modes):
310 if not self.initialized:
311 return
312
313 if not modes == '-z':
314 return
315
316 if channel in self.channels:
317 self.channels.remove(channel)
318 self.elog.request('Channel @b%s@b was dropped. Deleting it.' % channel)
319
320 def getCommands(self):
321 return self.commands_admin
322
323 def get_location(self, opts, arg, channel, sender):
324 nick = None
325 location = None
326
327 if 'nick' in opts:
328 nick = arg
329 elif arg == '':
330 nick = sender
331 else:
332 location = arg
333
334 if nick:
335 location = self.users.get(nick, 'location')
336
337 if not location:
338 if 'nick' in opts:
339 self.msg(channel, 'No location found linked to nick %s.' % arg)
340 else:
341 self.msg(channel, 'No location found linked to your nick. To link one type: @b.register_location <location>@b')
342
343 return location