]>
Commit | Line | Data |
---|---|---|
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 | ||
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 |