]>
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 | |
9c9f0247 | 23 | from api import bing, calc, google, imdb, ipinfo, lastfm, quotes, urbandictionary, urls, weather, wolfram, words, steam |
685e346e | 24 | from internets_utils import * |
9c9f0247 | 25 | from api.steam import SteamUser |
685e346e | 26 | |
2d09c59a | 27 | import pyva_net_rizon_acid_core_Acidictive as Acidictive |
f6353b7a | 28 | import pyva_net_rizon_acid_core_AcidCore as AcidCore |
2d09c59a | 29 | import pyva_net_rizon_acid_core_User as User |
f6353b7a | 30 | |
685e346e A |
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: | |
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 | ||
685e346e A |
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: | |
41ae6aae | 117 | self.bing = bing.Bing(self.config.get('internets').get('bing_appid')) |
685e346e A |
118 | except Exception, err: |
119 | self.log.exception('Error initializing internets bing API (%s)' % err) | |
120 | self.nsp = calc.NumericStringParser() | |
41ae6aae | 121 | self.google = google.Google(self.config.get('internets').get('key_google')) |
685e346e | 122 | self.imdb = imdb.Imdb() |
41ae6aae | 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')) | |
685e346e | 126 | self.urbandictionary = urbandictionary.UrbanDictionary() |
41ae6aae | 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')) | |
685e346e A |
132 | except Exception, err: |
133 | self.log.exception('Error initializing internets module (%s)' % err) | |
134 | raise | |
135 | ||
e7697283 A |
136 | for channel in self.channels.list_valid(): |
137 | self.join(channel.name) | |
685e346e | 138 | |
e7697283 | 139 | self.log.debug('Joined channels.') |
685e346e A |
140 | |
141 | try: | |
142 | self.start_threads() | |
143 | except Exception, err: | |
144 | self.log.exception('Error starting threads for internets module (%s)' % err) | |
145 | raise | |
146 | ||
147 | self.initialized = True | |
148 | self.online = True | |
149 | self.elog.debug('Started threads.') | |
150 | return True | |
151 | ||
685e346e A |
152 | def stop(self): |
153 | if hasattr(self, 'antiflood'): | |
154 | self.antiflood.stop() | |
155 | ||
156 | if hasattr(self, 'auth'): | |
157 | self.auth.stop() | |
158 | ||
159 | if hasattr(self, 'users'): | |
160 | if self.initialized: | |
161 | self.users.force() | |
162 | ||
163 | self.users.stop() | |
164 | self.users.db_close() | |
165 | ||
166 | if hasattr(self, 'channels'): | |
167 | if self.initialized: | |
168 | self.channels.force() | |
169 | ||
170 | self.channels.stop() | |
171 | self.channels.db_close() | |
172 | ||
173 | if hasattr(self, 'options'): | |
174 | if self.initialized: | |
175 | self.options.force() | |
176 | ||
177 | self.options.stop() | |
178 | self.options.db_close() | |
179 | ||
685e346e A |
180 | def errormsg(self, target, message): |
181 | self.msg(target, '@b@c4Error:@o %s' % message) | |
182 | ||
183 | def usagemsg(self, target, description, examples): | |
184 | message = '@errsep @bUsage@b %s @errsep' % description | |
185 | ||
186 | if examples != None: | |
187 | message += ' @bExamples@b %s @errsep' % ', '.join(examples) | |
188 | ||
189 | self.msg(target, message) | |
190 | ||
191 | def msg(self, target, message): | |
192 | if message != '': | |
2d09c59a | 193 | Acidictive.privmsg(self.nick, target, format_ascii_irc(message)) |
685e346e A |
194 | |
195 | def multimsg(self, target, count, intro, separator, pieces, outro = ''): | |
196 | cur = 0 | |
197 | ||
198 | while cur < len(pieces): | |
199 | self.msg(target, intro + separator.join(pieces[cur:cur + count]) + outro) | |
200 | cur += count | |
201 | ||
202 | def notice(self, target, message): | |
203 | if message != '': | |
2d09c59a | 204 | Acidictive.notice(self.nick, target, format_ascii_irc(message)) |
685e346e A |
205 | |
206 | def execute(self, manager, command, argument, channel, sender, userinfo): | |
207 | full_command = '%s%s' % (command, ' %s' % argument if len(argument) else '') | |
208 | cmd = manager.get_command(command) | |
209 | ||
210 | if cmd == None: | |
211 | self.msg(channel, manager.invalid) | |
212 | self.elog.debug('Parsed command @b%s@b: invalid command.' % full_command) | |
213 | return | |
214 | ||
215 | if self.users.is_banned(sender) or self.antiflood.check_user(sender, command, argument): | |
216 | user = self.users[sender] | |
217 | message = 'You were banned by @b%s@b.' % user.ban_source | |
218 | ||
219 | if user.ban_reason != None: | |
220 | message += ' Reason: @b%s@b.' % user.ban_reason | |
221 | ||
222 | if user.ban_expiry != None: | |
223 | message += ' Expires: @b%s@b.' % datetime.fromtimestamp(user.ban_expiry) | |
224 | ||
225 | self.notice(sender, message) | |
226 | self.elog.debug('Parsed command @b%s@b: user is banned.' % full_command) | |
227 | return | |
228 | ||
229 | self.elog.command('%s%s > %s' % (sender, ':%s' % channel if channel != sender else '', full_command)) | |
230 | ||
231 | parser = erepparser.ErepublikParser(add_help_option = False, option_class = erepparser.ErepublikParserOption) | |
232 | cmd_type = cmd[1] | |
233 | cmd_args = cmd[3] | |
234 | ||
235 | parser.add_option('-?', '--help', action = 'store_true') | |
236 | ||
237 | for cmd_arg in cmd_args: | |
238 | parser.add_option(cmd_arg[1], '--' + cmd_arg[0], **cmd_arg[3]) | |
239 | ||
240 | try: | |
241 | (popts, pargs) = parser.parse_args(args = argument.split(' ')) | |
242 | except erepparser.ErepublikParserError, err: | |
243 | self.msg(channel, str(err)) #TODO: Avoid str, use unicode. | |
244 | parser.destroy() | |
245 | self.elog.debug('Parsed command @b%s@b: invalid options.' % full_command) | |
246 | return | |
247 | ||
248 | if popts.help == True: | |
249 | manager.commands['help'][0](self, manager, {}, command, channel, sender) | |
250 | parser.destroy() | |
251 | self.elog.debug('Parsed command @b%s@b: help intercepted.' % full_command) | |
252 | return | |
253 | ||
254 | opt_dict = {} | |
255 | larg = ' '.join(pargs).strip() | |
256 | is_offline = True | |
257 | ||
258 | for cmd_arg in cmd_args: | |
259 | parg = getattr(popts, cmd_arg[0]) | |
260 | ||
261 | if parg != None: | |
262 | if len(cmd_arg) <= 4 or not (cmd_arg[4] & cmd_manager.ARG_OFFLINE): | |
263 | is_offline = False | |
264 | ||
265 | if len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_YES) and larg == '': | |
266 | self.msg(channel, 'Error: %s option requires an argument.' % cmd_arg[1]) | |
267 | parser.destroy() | |
268 | self.elog.debug('Parsed command @b%s@b: option constraint was broken.' % full_command) | |
269 | return | |
270 | ||
271 | opt_dict[cmd_arg[0]] = parg | |
272 | elif len(cmd_arg) > 4 and (cmd_arg[4] & cmd_manager.ARG_OFFLINE and cmd_arg[4] & cmd_manager.ARG_OFFLINE_REQ): | |
273 | is_offline = False | |
274 | ||
275 | if not self.online and ((len(pargs) > 0 and not (cmd_type & cmd_manager.ARG_OFFLINE)) or not is_offline): | |
276 | self.notice(sender, 'The eRepublik API is offline. Please retry later.') | |
277 | parser.destroy() | |
278 | self.elog.debug('Parsed command @b%s@b: offline.' % full_command) | |
279 | return | |
280 | ||
281 | if (cmd_type & cmd_manager.ARG_YES) and (larg == None or larg == ''): | |
282 | self.notice(sender, '@bUsage@b: %s @b%s@b' % (command, cmd[4] if len(cmd) > 4 else 'argument')) | |
283 | else: | |
284 | try: | |
285 | cmd[0](self, manager, opt_dict, larg, channel, sender, userinfo) | |
286 | except Exception, e: | |
287 | tb = traceback.extract_tb(sys.exc_info()[2]) | |
288 | longest = 0 | |
289 | ||
290 | for entry in tb: | |
291 | length = len(entry[2]) | |
292 | ||
293 | if length > longest: | |
294 | longest = length | |
295 | ||
296 | self.elog.exception('%s%s > @b%s@b: %s' % (sender, ':%s' % channel if channel != sender else '', full_command, e)) | |
297 | self.log.exception("internets error!") | |
298 | ||
299 | for entry in tb: | |
300 | self.elog.traceback('@b%-*s@b : %d %s' % (longest, entry[2], entry[1], entry[3])) | |
301 | ||
302 | 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.') | |
303 | ||
304 | parser.destroy() | |
305 | self.elog.debug('Parsed command @b%s@b: execution terminated.' % full_command) | |
306 | ||
307 | def onChanModes(self, prefix, channel, modes): | |
308 | if not self.initialized: | |
309 | return | |
310 | ||
311 | if not modes == '-z': | |
312 | return | |
313 | ||
314 | if channel in self.channels: | |
315 | self.channels.remove(channel) | |
316 | self.elog.request('Channel @b%s@b was dropped. Deleting it.' % channel) | |
317 | ||
318 | def getCommands(self): | |
319 | return self.commands_admin | |
320 | ||
321 | def get_location(self, opts, arg, channel, sender): | |
322 | nick = None | |
323 | location = None | |
324 | ||
325 | if 'nick' in opts: | |
326 | nick = arg | |
327 | elif arg == '': | |
328 | nick = sender | |
329 | else: | |
330 | location = arg | |
331 | ||
332 | if nick: | |
333 | location = self.users.get(nick, 'location') | |
334 | ||
335 | if not location: | |
336 | if 'nick' in opts: | |
337 | self.msg(channel, 'No location found linked to nick %s.' % arg) | |
338 | else: | |
9c9f0247 | 339 | 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 |
340 | |
341 | return location | |
9c9f0247 M |
342 | |
343 | def get_steamid(self, opts, arg, channel, sender): | |
344 | """Gets the steamid from the database.""" | |
345 | nick = None | |
346 | steamid = None | |
347 | ||
348 | if 'nick' in opts: | |
349 | nick = arg | |
350 | else: | |
351 | nick = sender | |
352 | ||
353 | steamid = self.users.get(nick, 'steamid') | |
354 | ||
355 | if not steamid: | |
356 | if 'nick' in opts: | |
357 | self.msg(channel, 'No steamid found linked to nick %s.' % arg) | |
358 | return | |
359 | else: | |
360 | self.msg(channel, 'No steamid found linked to your nick. To link one, type: @b%sregister_steam <steamid>@b' % self.commands_user.get_prefix()) | |
361 | return | |
362 | else: | |
363 | return SteamUser(nick, steamid) |