NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

svc_slash.py (80240B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\svc_slash.py
      2 from future.utils import raise_
      3 import logging
      4 import os
      5 import random
      6 import sys
      7 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
      8 from carbonui.primitives.containerAutoSize import ContainerAutoSize
      9 from collections import defaultdict
     10 import blue
     11 import dogma.data
     12 import evetypes
     13 import localization
     14 import log
     15 import trinity
     16 import triui
     17 import uiblinker
     18 import uthread
     19 import utillib
     20 from avatardisplay.avatardisplay import AvatarDisplay as ad
     21 from carbon.common.script.sys import sessions
     22 from carbon.common.script.sys.service import Service
     23 from carbon.common.script.sys.serviceConst import ROLE_ANY, ROLE_LOGIN, ROLE_PLAYER, ROLE_WORLDMOD, SERVICE_RUNNING, SERVICE_START_PENDING
     24 from carbonui import uiconst
     25 from carbonui.primitives.container import Container
     26 from carbonui.uicore import uicore
     27 from controltowerresources.data import get_control_tower_resources
     28 from eve.client.script.ui.control import eveEdit, eveLabel
     29 from carbonui.button.group import ButtonGroup
     30 from carbonui.control.window import Window
     31 from player_messaging.client.ui_message import prompt_player, prompt_player_fsd_dialog
     32 from eve.client.script.ui.util import uix
     33 from eve.common.lib import appConst as const
     34 from eve.common.script.net import eveMoniker
     35 from eve.common.script.sys import eveCfg
     36 from eve.devtools.script import param
     37 from eve.devtools.script.slashError import Error
     38 from eveexceptions import UserError
     39 from eveprefs import boot
     40 from eveservices.menu import GetMenuService
     41 from eveservices.xmppchat import GetChatService
     42 from inventorycommon.const import typePlex
     43 from inventorycommon.typeHelpers import GetAveragePrice
     44 import plex.client.api.repository as plex_api
     45 from slash.client.event_publisher import SlashEventPublisher
     46 from player_messaging import IGNORE_TO_TOP
     47 from uihighlighting.const import DIRECTION_STRING_TO_ID
     48 from uihighlighting.ui.uiutil import UI_CLASS_BY_NAME
     49 logger = logging.getLogger('svc_slash')
     50 SERVICENAME = 'slash'
     51 TOKEN = '$'
     52 aliasFile = 'aliases.ini'
     53 macroFile = 'macros.ini'
     54 version = 2.4
     55 
     56 def Message(title, body, icon = triui.INFO):
     57     prompt_player(body, title, options=uiconst.OK, icon=icon)
     58 
     59 
     60 def AsyncMessage(*args):
     61     uthread.new(Message, *args)
     62 
     63 
     64 def Progress(title, text, current, total):
     65     sm.GetService('loading').ProgressWnd(title, text, current, total)
     66 
     67 
     68 class InlineFunctionResolver():
     69 
     70     def __init__(self):
     71         pass
     72 
     73     def __getitem__(self, func):
     74         if ':' in func:
     75             func, args = func.split(':', 1)
     76         else:
     77             args = ()
     78         method = getattr(self, 'f_' + func.lower(), None)
     79         if func is None:
     80             raise Error("unknown inline function '%s'" % func)
     81         if args:
     82             args = args.split(',')
     83         return method(*args)
     84 
     85     def _getinvrow(self, *args):
     86         if len(args) == 1:
     87             containerID = session.shipid
     88             flag = args[0]
     89         elif len(args) == 2:
     90             if args[0].lower() == 'me':
     91                 containerID = session.shipid
     92             else:
     93                 containerID = int(args[0])
     94             flag = args[1]
     95         flag = getattr(const, 'flag' + flag.capitalize())
     96         for rec in sm.GetService('invCache').GetInventoryFromId(containerID).List():
     97             if rec.flag == flag:
     98                 return rec
     99 
    100     def f_itemid(self, *args, **kw):
    101         return self._getinvrow(*args).itemID
    102 
    103     def f_typeid(self, *args):
    104         return self._getinvrow(*args).typeID
    105 
    106 
    107 def GetCharacter(name, ignoreNotFound = False):
    108     result = sm.RemoteSvc('lookupSvc').LookupCharacters(name, 1)
    109     if result:
    110         cfg.eveowners.Prime([ each.characterID for each in result ])
    111         return result[0]
    112     try:
    113         return cfg.eveowners.Get(int(name))
    114     except:
    115         if ignoreNotFound:
    116             return None
    117         AsyncMessage('No such character', 'Character not found:<br>  %s' % name)
    118         raise UserError(IGNORE_TO_TOP)
    119 
    120 
    121 def extract_prefix_from(prefix, original):
    122     if original.startswith(prefix):
    123         return original[len(prefix):]
    124     raise Error("Couldn't extract the prefix {} from {}".format(prefix, original))
    125 
    126 
    127 def parse_params_for_gag_or_ban(p):
    128     try:
    129         channel_specifier, char, reason, durationInMinutes = p.Parse('sssi')
    130     except Error:
    131         channel_specifier, char, reason = p.Parse('sss')
    132         durationInMinutes = 30
    133 
    134     return (channel_specifier,
    135      char,
    136      reason,
    137      durationInMinutes)
    138 
    139 
    140 def extract_arguments_for_gag_or_ban(p):
    141     channel_specifier, char, reason, durationInMinutes = parse_params_for_gag_or_ban(p)
    142     durationInSecs = durationInMinutes * 60
    143     channelName = extract_prefix_from('channelName=', channel_specifier)
    144     charID = GetCharacter(char).characterID
    145     return (channelName,
    146      charID,
    147      reason,
    148      durationInSecs)
    149 
    150 
    151 def extract_arguments_for_ungag_or_unban(p):
    152     channel_specifier, char = p.Parse('ss')
    153     channelName = extract_prefix_from('channelName=', channel_specifier)
    154     charID = GetCharacter(char).characterID
    155     return (channelName, charID)
    156 
    157 
    158 class SlashService(Service):
    159     __guid__ = 'svc.slash'
    160     __notifyevents__ = ['ProcessRestartUI', 'ProcessUIRefresh']
    161     __slashhook__ = True
    162     __startupdependencies__ = ['publicGatewaySvc']
    163 
    164     def __init__(self):
    165         Service.__init__(self)
    166         self.patched = False
    167         self.aliases = {}
    168         self.macros = {}
    169         self.typeIDsByName = None
    170         self._slashEventPublisher = None
    171 
    172     def Run(self, _memStream = None):
    173         self.state = SERVICE_START_PENDING
    174         try:
    175             self.wnd = None
    176             self.history = []
    177             self.historyPtr = -1
    178             self.lastslash = ''
    179             self.busy = self.aborted = False
    180             self.jobs = []
    181             self.LoadMacrosAndAliases()
    182             self.Clear()
    183         except:
    184             log.LogException()
    185             sys.exc_clear()
    186 
    187         self._slashEventPublisher = SlashEventPublisher(self.publicGatewaySvc)
    188         self.state = SERVICE_RUNNING
    189 
    190     def GetMacros(self):
    191         self.LoadMacrosAndAliases()
    192         return self.macros.items()
    193 
    194     def LoadMacrosAndAliases(self):
    195         defaultMacros = {'GMH: Unload All': '/unload me all',
    196          'WM: Remove All Drones': '/unspawn range=500000 only=categoryDrone',
    197          'WM: Remove All Fighters': '/unspawn range=500000 only=categoryFighter',
    198          'WM: Remove All Wrecks': '/unspawn range=500000 only=groupWreck',
    199          'WM: Remove CargoContainers': '/unspawn range=500000 only=groupCargoContainer,groupFreightContainer',
    200          'WM: Remove SecureContainers': '/unspawn range=500000 only=groupSecureCargoContainer,groupAuditLogSecureContainer',
    201          'HEALSELF: Repair My Ship': '/heal',
    202          'HEALSELF: Repair My Modules': '/repairmodules',
    203          'GMH: Online My Modules': '/online me',
    204          'GML: Session Change 5sec': '/sessionchange 5'}
    205         self.aliases = self.LoadStuff(aliasFile)
    206         if len(self.aliases) == 0:
    207             self.aliases = {}
    208         self.macros = self.LoadStuff(macroFile)
    209         self.macros.update(defaultMacros)
    210 
    211     def LoadStuff(self, fileName):
    212         targetFile = os.path.join(sm.StartService('insider').GetInsiderDir(), fileName)
    213         if not os.path.exists(targetFile):
    214             return {}
    215         d = {}
    216         lines = blue.AtomicFileRead(targetFile).replace('\r', '').replace('\x00', '').split('\n')
    217         for line in lines:
    218             aliasName, comseq = line.split('=', 1)
    219             d[aliasName.strip()] = comseq.strip()
    220 
    221         return d
    222 
    223     def SaveStuff(self, thisDict, fileName):
    224         text = '\r\n'.join([ '%s=%s' % (aliasName, comseq) for aliasName, comseq in thisDict.iteritems() ])
    225         targetFile = os.path.join(sm.StartService('insider').GetInsiderDir(), fileName)
    226         blue.AtomicFileWrite(targetFile, text)
    227 
    228     def Stop(self, memStream = None):
    229         if self.wnd and not self.wnd.destroyed:
    230             self.Hide()
    231         Service.Stop(self, memStream)
    232 
    233     def ConstructLayout(self):
    234         self.wnd = wnd = Window.GetIfOpen(windowID='slashcon')
    235         if wnd:
    236             self.wnd.Maximize()
    237             return
    238         self.Layout()
    239 
    240     def Layout(self):
    241         self.wnd = wnd = Window.Open(windowID='slashcon')
    242         wnd.DoClose = self.Hide
    243         wnd.SetCaption('Slash')
    244         wnd.OnUIRefresh = None
    245         self.main = Container(name='con', parent=wnd.GetChild('main'))
    246         self.input_container = Container(name='input', parent=self.main, align=uiconst.TOBOTTOM, height=20)
    247         self.control_container = ContainerAutoSize(name='control', parent=self.main, align=uiconst.TOBOTTOM, padBottom=8, callback=self.ApplyWindowMinSize, only_use_callback_when_size_changes=True)
    248         self.output_container = Container(name='output', parent=self.main, align=uiconst.TOALL)
    249         self.ConstructCommandInput()
    250         self.ConstructButtonGroup()
    251         self.ConstructOutput()
    252         self.ApplyWindowMinSize()
    253         uicore.registry.SetFocus(self.input)
    254 
    255     def ConstructOutput(self):
    256         output = self.wnd.sr.output = eveEdit.Edit(setvalue=self.outputcontents, parent=self.output_container, readonly=1)
    257         output.autoScrollToBottom = 1
    258 
    259     def ConstructCommandInput(self):
    260         eveLabel.Label(text='Command: ', parent=self.input_container, align=uiconst.TOLEFT, color=None, state=uiconst.UI_DISABLED, singleline=1)
    261         self.input = self.wnd.sr.input = SingleLineEditText(name='slashCmd', parent=self.input_container, left=0, width=0, align=uiconst.TOALL)
    262         self.input.OnKeyDown = (self.InputKey, self.input)
    263         self.input.OnReturn = self.InputEnter
    264 
    265     def ConstructButtonGroup(self):
    266         buttons = ButtonGroup(parent=self.control_container, align=uiconst.CENTER)
    267         buttons.AddButton('Clear', self.Clear)
    268         buttons.AddButton('Exec Last', self.ExLast)
    269         buttons.AddButton('Exec Once', self.ExOnce)
    270         buttons.AddButton('Exec Loop', self.ExLoop)
    271         buttons.AddButton('Abort', self.ExAbort)
    272 
    273     def ApplyWindowMinSize(self):
    274         width, height = self.wnd.GetWindowSizeForContentSize(height=self.control_container.height, width=self.control_container.width)
    275         self.wnd.SetMinSize([width, 224])
    276 
    277     def Hide(self, *args):
    278         if self.wnd:
    279             self.slashcontents = self.wnd.sr.input.GetValue()
    280             self.wnd.Close()
    281         self.wnd = None
    282 
    283     def ProcessRestartUI(self):
    284         if self.wnd:
    285             self.Hide()
    286             self.ConstructLayout()
    287 
    288     def ProcessUIRefresh(self):
    289         if self.wnd:
    290             self.wnd.Close()
    291             self.ConstructLayout()
    292 
    293     def Clear(self, *args):
    294         cmds = []
    295         import svc
    296         for sname in svc.__dict__:
    297             s = getattr(svc, sname)
    298             if getattr(s, '__slashhook__', False):
    299                 for m in dir(s):
    300                     if m.startswith('cmd_'):
    301                         cmds.append(m[4:])
    302 
    303         cmds.sort()
    304         self.outputcontents = 'Commands: <font color="#ffc000">%s</font><br>' % (', '.join(cmds) or 'None')
    305         if self.aliases:
    306             cmds = self.aliases.keys()
    307             cmds.sort()
    308             self.outputcontents += 'Aliases: <font color="#00ffc0">%s</font><br>' % ', '.join(cmds)
    309         self.outputcontents += 'Note: Slash commands, extended commands and user-defined aliases all work both in chat and in console. Commands that require a chat channel cannot be run in the console.<br>'
    310         if self.wnd:
    311             self.wnd.sr.output.SetText(self.outputcontents)
    312 
    313     def Echo(self, text):
    314         self.outputcontents = '%s%s' % (self.outputcontents, text)
    315         if self.wnd:
    316             self.wnd.sr.output.SetText(self.outputcontents)
    317 
    318     def InputKey(self, otherself, key, flag):
    319         CTRL = uicore.uilib.Key(uiconst.VK_CONTROL)
    320         if CTRL and key == uiconst.VK_UP:
    321             self.InputHistory(-1)
    322         elif CTRL and key == uiconst.VK_DOWN:
    323             self.InputHistory(1)
    324         else:
    325             SingleLineEditText.OnKeyDown(otherself, key, flag)
    326 
    327     def InputHistory(self, offset):
    328         if self.history:
    329             self.historyPtr += offset
    330             if self.historyPtr < -len(self.history):
    331                 self.historyPtr = -len(self.history)
    332                 return
    333             if self.historyPtr >= 0:
    334                 self.historyPtr = 0
    335                 self.wnd.sr.input.SetValue('')
    336                 return
    337             self.wnd.sr.input.SetValue(self.history[self.historyPtr])
    338 
    339     def InputEnter(self, *_args):
    340         try:
    341             self.lastslash = self.wnd.sr.input.GetValue()
    342         except:
    343             return
    344 
    345         self.history.append(self.lastslash)
    346         self.historyPtr = 0
    347         self.wnd.sr.input.SetValue('')
    348         self.Ex(self.lastslash)
    349 
    350     def ExLoop(self, *_args):
    351         try:
    352             cmd = self.wnd.sr.input.GetValue().strip()
    353         except:
    354             return
    355 
    356         if len(cmd) > 1:
    357             result = uix.QtyPopup(maxvalue=50, minvalue=1, caption='Looped Execution', label='', hint='Specify number of times to execute:<br><br> %s<br><br>Note:<br>- Max. 50 iterations<br>- USE WITH CARE!' % cmd)
    358             if result:
    359                 amount = result['qty']
    360                 self.lastslash = cmd
    361                 self.wnd.sr.input.SetValue('')
    362                 self.Ex(cmd, amount)
    363 
    364     def ExLast(self, *_args):
    365         if self.lastslash:
    366             self.Ex(self.lastslash)
    367 
    368     def ExOnce(self, *_args):
    369         try:
    370             self.lastslash = self.wnd.sr.input.GetValue()
    371         except:
    372             return
    373 
    374         self.Ex(self.lastslash)
    375 
    376     def ExAbort(self, *_args):
    377         if self.busy and not self.aborted:
    378             self.Echo('<font color="#ffff00">*** Aborting...</font><br>')
    379         self.aborted = True
    380         self.busy = False
    381 
    382     def Ex(self, this, count = 1):
    383         if not this:
    384             return
    385         if this[0] != '/':
    386             this = '/' + this
    387         self.jobs.append((this, count))
    388         if self.busy:
    389             if count > 0:
    390                 self.Echo('<font color="#ffff00">*** Queued(%d): %s</font><br>' % (count, this))
    391             else:
    392                 self.Echo('<font color="#ffff00">*** Queued: %s</font><br>' % this)
    393             return
    394         self.JobHandler()
    395 
    396     def JobHandler(self):
    397         try:
    398             self.aborted = False
    399             while self.jobs and not self.aborted:
    400                 this, count = self.jobs.pop(0)
    401                 isLoop = count > 1
    402                 try:
    403                     self.busy = True
    404                     self.aborted = False
    405                     if isLoop:
    406                         self.Echo('<font color="#ffff00">*** Loop started (%d iterations)</font><br>' % count)
    407                     while count and not self.aborted:
    408                         self.Echo('<font color="#00ffff">slash: %s</font><br>' % this)
    409                         try:
    410                             res = sm.GetService('slash').SlashCmd(this)
    411                             if res and len(str(res)) > 5000:
    412                                 res = str(res)[:5000] + '...'
    413                             self.Echo('<font color="#00ff55">slash result: %s</font><br>' % res)
    414                         except UserError as e:
    415                             if e.args[0] in ('SlashError',):
    416                                 self.Echo('<font color="#ff5500">slash error: %s</font><br>' % e.dict['reason'])
    417                                 break
    418                             else:
    419                                 self.Echo('<font color="#00ff55">slash result: None</font><br>')
    420                                 raise_(UserError, e)
    421 
    422                         count -= 1
    423                         blue.pyos.synchro.SleepWallclock(100)
    424 
    425                     if isLoop:
    426                         if self.aborted:
    427                             reason = 'Aborted by user'
    428                         elif count != 0:
    429                             reason = 'Error condition'
    430                         else:
    431                             reason = 'Batch completed'
    432                         self.Echo('<font color="#ffff00">*** Loop terminated (%s)</font><br>' % reason)
    433                 finally:
    434                     self.busy = False
    435 
    436             else:
    437                 if self.jobs:
    438                     self.Echo('<font color="#ffff00">*** Slash command queue flushed</font><br>')
    439                     self.jobs = []
    440 
    441         finally:
    442             self.aborted = False
    443 
    444     def FetchRemoteCommandList(self):
    445         if hasattr(self, 'remoteCommandList'):
    446             return
    447         try:
    448             sm.RemoteSvc('slash').SlashCmd('/')
    449         except UserError as e:
    450             try:
    451                 msg = e.args[1]['reason']
    452                 cmds = eval(msg.split(': ')[1])
    453                 cmds.sort()
    454             except:
    455                 cmds = []
    456                 log.LogException()
    457                 Message('Hmmm', "Unable to acquire server slash command list. Not particularly bad, just means command autocompletion won't work")
    458                 sys.exc_clear()
    459 
    460             sys.exc_clear()
    461 
    462         self.remoteCommandList = cmds
    463 
    464     def MatchCmd(self, command):
    465         ret = []
    466         localMatch = False
    467         for cmd in self.aliases:
    468             if cmd.startswith(command):
    469                 if cmd == command:
    470                     return ([cmd], True)
    471                 if cmd not in ret:
    472                     ret.append(cmd)
    473                 localMatch = True
    474 
    475         for cmd in self.remoteCommandList:
    476             if cmd.startswith(command):
    477                 if cmd == command:
    478                     return ([cmd], False)
    479                 ret.append(cmd)
    480 
    481         import svc
    482         for sname in svc.__dict__:
    483             s = getattr(svc, sname)
    484             if getattr(s, '__slashhook__', False):
    485                 for f in s.__dict__:
    486                     if f.startswith('cmd_' + command):
    487                         cmd = f[4:]
    488                         if cmd == command:
    489                             return ([cmd], True)
    490                         if cmd not in ret:
    491                             ret.append(cmd)
    492                         localMatch = True
    493 
    494         return (ret, localMatch)
    495 
    496     def SlashCmd(self, command, fallThrough = True, isMacro = False):
    497         if command.startswith('//'):
    498             return sm.RemoteSvc('slash').SlashCmd(command[1:])
    499         return self._SlashCmd(command, fallThrough, isMacro)
    500 
    501     def _SlashCmd(self, txt, fallThrough, isMacro):
    502         if isMacro:
    503             self.FetchRemoteCommandList()
    504             command = None
    505         else:
    506             parts = txt.split(' ', 1)
    507             command = parts[0].strip().lower()
    508             if len(parts) == 2:
    509                 args = parts[1].strip()
    510             else:
    511                 args = ''
    512             if command[0] == '/':
    513                 command = command[1:]
    514             self.FetchRemoteCommandList()
    515             if self.remoteCommandList is not None:
    516                 matches, hasLocal = self.MatchCmd(command)
    517                 if hasLocal:
    518                     if len(matches) == 1:
    519                         command = matches[0]
    520                     else:
    521                         raise Error('%s is ambiguous. It resolves to multiple commands or aliases: %s' % (command, matches))
    522         if command in self.aliases or isMacro:
    523             if isMacro:
    524                 sequence = txt
    525             else:
    526                 sequence = self.aliases[command]
    527             sequence = sequence.split(';')
    528             for line in sequence:
    529                 line = line.strip()
    530                 if line:
    531                     newline = []
    532                     p = param.ParamObject(txt)
    533                     for part in line.split(TOKEN):
    534                         if newline:
    535                             if len(part) >= 1:
    536                                 if part[0] in '0123456789':
    537                                     try:
    538                                         if len(part) >= 2 and part[1] == '-':
    539                                             part = p[int(part[0]):] + part[2:]
    540                                         else:
    541                                             part = p[int(part[0])] + part[1:]
    542                                     except param.Error:
    543                                         raise Error('Alias expected additional parameter(s)')
    544 
    545                                 else:
    546                                     part = TOKEN + part
    547                         newline.append(part)
    548 
    549                     newline = ''.join(newline) % InlineFunctionResolver()
    550                     res = self.SlashCmd(newline)
    551 
    552             if len(sequence) == 1:
    553                 return res
    554             return 'Ok'
    555         try:
    556             args = args % InlineFunctionResolver()
    557             commandLine = '/' + command + ' ' + args
    558         except Error:
    559             raise
    560         except:
    561             log.LogException()
    562             raise Error('unexpected error resolving inline function')
    563 
    564         import svc
    565         for raw_service_name in svc.__dict__:
    566             service_classobj = getattr(svc, raw_service_name)
    567             if getattr(service_classobj, '__slashhook__', False):
    568                 command_name = 'cmd_' + command
    569                 slash_func = getattr(service_classobj, command_name, None)
    570                 if slash_func:
    571                     try:
    572                         service_name = service_classobj.__guid__[4:]
    573                         service_instance = sm.GetService(service_name)
    574                         function_to_call = getattr(service_instance, command_name)
    575                         ret = function_to_call(param.ParamObject(args))
    576                         if ret:
    577                             return ret
    578                         break
    579                     except param.Error as e:
    580                         if slash_func.__doc__:
    581                             raise Error('usage: /%s %s' % (command, slash_func.__doc__.replace('%s', command)))
    582                         else:
    583                             raise Error('/%s called with crap args and doesnt handle it properly' % command)
    584 
    585         if fallThrough:
    586             return sm.RemoteSvc('slash').SlashCmd(commandLine)
    587 
    588     def cmd_sethackingdifficulty(self, p):
    589         sm.GetService('hackingUI').SetDifficulty(p.Parse('i')[0])
    590         return 'Ok'
    591 
    592     def cmd_sethackingvirusstats(self, p):
    593         integers = p.Parse('iii')
    594         sm.GetService('hackingUI').SetVirusStats(integers[0], integers[1], integers[2])
    595         return 'Ok'
    596 
    597     def cmd_openhacking(self, p):
    598         sm.GetService('hackingUI').TriggerNewGame()
    599 
    600     def cmd_run(self, p):
    601         filename, = p.Parse('s')
    602         if not os.path.exists(filename):
    603             filename += '.txt'
    604             if not os.path.exists(filename):
    605                 raise Error('file not found')
    606         for line in open(filename, 'r'):
    607             line = line.strip()
    608             if line.startswith('/'):
    609                 self.SlashCmd(line)
    610 
    611         return 'Ok'
    612 
    613     def cmd_macromenu(self, p):
    614         return self.cmd_alias(p, True)
    615 
    616     def cmd_alias(self, p, isMacro = False):
    617         what, rest = p.Parse('s?r')
    618         what = what.lower()
    619         if isMacro:
    620             thing = 'Macro'
    621             catalog = self.macros
    622             file = macroFile
    623         else:
    624             thing = 'Alias'
    625             catalog = self.aliases
    626             file = aliasFile
    627         if what == 'add':
    628             what, aliasName, rest = p.Parse('ssr')
    629             for key in catalog.keys():
    630                 if key.lower() == aliasName.lower():
    631                     action = 'modified'
    632                     break
    633             else:
    634                 action = 'added'
    635 
    636             catalog[aliasName] = rest
    637             self.SaveStuff(catalog, file)
    638             return '%s %s %s' % (thing, aliasName, action)
    639         if what == 'del':
    640             if not rest:
    641                 raise param.Error
    642             aliasName = rest.lower().strip('"')
    643             for key in catalog.keys():
    644                 if key.lower() == aliasName:
    645                     del catalog[aliasName]
    646                     self.SaveStuff(catalog, file)
    647                     return '%s %s deleted' % (thing, aliasName)
    648             else:
    649                 raise Error('No such %s: %s' % (thing, aliasName))
    650 
    651         elif what == 'list':
    652             cmds = catalog.keys()
    653             cmds.sort()
    654             return '%s: <font color="#00ffc0">%s</font><br>' % (thing, ', '.join(cmds))
    655         for key, item in catalog.iteritems():
    656             if key.lower() == what.lower():
    657                 return '%s %s is defined as: %s' % (thing, what, item)
    658 
    659         raise param.Error
    660 
    661     def cmd_loop(self, p):
    662         count, rest = p.Parse('ir')
    663         if count >= 10:
    664             if count > 50:
    665                 Message('Looped Execution', 'The number of iterations must be less than 50')
    666                 return
    667             result = prompt_player(title='Looped Execution', text='You have specified %d iterations.<br>Slash commands looped this way cannot be aborted. Depending on the slash command, it can take a long time to complete or cause a high server load.<br>Continue?' % count, options=uiconst.OKCANCEL, icon=uiconst.WARNING)
    668             if result in (uiconst.ID_CANCEL, uiconst.ID_CLOSE):
    669                 return
    670         for i in xrange(count):
    671             self.SlashCmd(rest)
    672 
    673         return 'Ok'
    674 
    675     def cmd_chtgag(self, p):
    676         channelName, charID, reason, durationInSecs = extract_arguments_for_gag_or_ban(p)
    677         GetChatService().MuteUser(channelName, charID, reason, durationInSecs)
    678         return 'Ok'
    679 
    680     def cmd_chtungag(self, p):
    681         channelName, charID = extract_arguments_for_ungag_or_unban(p)
    682         GetChatService().UnmuteUser(channelName, charID)
    683         return 'Ok'
    684 
    685     def cmd_chtban(self, p):
    686         channelName, charID, reason, durationInSecs = extract_arguments_for_gag_or_ban(p)
    687         GetChatService().BanUserFromChannel(channelName, charID, reason, durationInSecs)
    688         return 'Ok'
    689 
    690     def cmd_chtunban(self, p):
    691         channelName, charID = extract_arguments_for_ungag_or_unban(p)
    692         GetChatService().UnbanUserFromChannel(channelName, charID)
    693         return 'Ok'
    694 
    695     def cmd_whoami(self, p):
    696         roles = []
    697         for k, v in globals().iteritems():
    698             if k.startswith('ROLE_'):
    699                 if type(v) in (int, long):
    700                     if session.role & v and v not in (ROLE_ANY, ROLE_PLAYER, ROLE_LOGIN):
    701                         roles.append(k[5:])
    702 
    703         text = ['character: %s (%s)' % (session.charid, cfg.eveowners.Get(session.charid).name), 'user: %s (type: %s)' % (session.userid, session.userType), 'role: 0x%08X (%s)' % (session.role, ', '.join(roles))]
    704         AsyncMessage('Account Information', '<br>'.join(text))
    705         return 'Ok'
    706 
    707     def cmd_hop(self, p):
    708         if session.role & ROLE_WORLDMOD:
    709             return
    710         dist, = p.Parse('i')
    711         if session.stationid:
    712             raise Error("This obviously won't work in a station :)")
    713         bp = sm.GetService('michelle').GetBallpark()
    714         me = bp.GetBall(bp.ego)
    715         v = trinity.TriVector(me.vx, me.vy, me.vz)
    716         v.Normalize()
    717         v.Scale(dist)
    718         sm.RemoteSvc('slash').SlashCmd('/tr me me offset=%d,%d,%d' % (int(v.x), int(v.y), int(v.z)))
    719         GetMenuService().ClearAlignTargets()
    720         return 'Ok'
    721 
    722     def cmd_online(self, p):
    723         target, = p.Parse('s')
    724         activeShipID = eveCfg.GetActiveShip()
    725         if session.stationid is not None and (target == 'me' or int(target) == activeShipID):
    726             dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    727             for module in dogmaLocation.dogmaItems[activeShipID].GetFittedItems().itervalues():
    728                 if const.effectOnline in (row.effectID for row in dogma.data.get_type_effects(module.typeID)):
    729                     try:
    730                         dogmaLocation.OnlineModule(module.itemID)
    731                     except UserError as e:
    732                         if e.msg.startswith('EffectAlreadyActive'):
    733                             continue
    734                         raise
    735 
    736         else:
    737             sm.RemoteSvc('slash').SlashCmd('/online %s' % target)
    738         return 'Ok'
    739 
    740     def cmd_super(self, p):
    741         target, = p.Parse('s')
    742         if target.lower() == 'me':
    743             victim = 'you'
    744             target = 'me'
    745         else:
    746             char = GetCharacter(target)
    747             victim = '"' + char.characterName + '"'
    748             target = char.characterID
    749         Progress('Making %s Leet' % victim, 'Please wait...', 0, 1)
    750         try:
    751             sm.RemoteSvc('slash').SlashCmd('/giveskill %s super 5' % target)
    752         finally:
    753             Progress('Making %s Leet' % victim, 'Done!', 1, 1)
    754 
    755         return 'Ok'
    756 
    757     def cmd_noob(self, p):
    758         target, = p.Parse('s')
    759         if target.lower() == 'me':
    760             victim = 'you'
    761             target = 'me'
    762         else:
    763             char = GetCharacter(target)
    764             victim = '"' + char.characterName + '"'
    765             target = char.characterID
    766         Progress('Making %s n00b' % victim, 'Please wait...', 0, 1)
    767         try:
    768             sm.RemoteSvc('slash').SlashCmd('/removeskill %s all' % target)
    769             sm.GetService('skillqueue').ReInitialize()
    770         finally:
    771             Progress('Making %s n00b' % victim, 'Done!', 1, 1)
    772 
    773         return 'Ok'
    774 
    775     def cmd_givestarterskills(self, p):
    776         try:
    777             target, = p.Parse('s')
    778             if target.lower() == 'me':
    779                 victim = 'you'
    780                 target = session.charid
    781             else:
    782                 char = GetCharacter(target)
    783                 victim = '"' + char.characterName + '"'
    784                 target = char.characterID
    785         except param.Error:
    786             victim = 'you'
    787             target = session.charid
    788 
    789         Progress('Giving %s starter skills' % victim, 'Please wait...', 0, 1)
    790         try:
    791             sm.RemoteSvc('slash').SlashCmd('/givestarterskills %s' % target)
    792         finally:
    793             Progress('Giving %s starter skills' % victim, 'Done!', 1, 1)
    794 
    795         return 'Ok'
    796 
    797     def cmd_randomizeskills(self, p):
    798         Progress('Randomizing your skills,', 'Please wait...', 0, 1)
    799         try:
    800             sm.RemoteSvc('slash').SlashCmd('/randomizeskills')
    801         finally:
    802             Progress('Randomizing your skills,', 'Done!', 1, 1)
    803 
    804         return 'Ok'
    805 
    806     def cmd_bp(self, p):
    807         try:
    808             name, rest = p.Parse('s?r')
    809             if rest is None:
    810                 rest = ''
    811         except param.Error:
    812             rest = ''
    813             try:
    814                 name, = p.Parse('r')
    815             except param.Error:
    816                 return
    817 
    818         typeID = self.AutoComplete(name, allowedCategories=[const.categoryBlueprint])
    819         if typeID:
    820             return sm.RemoteSvc('slash').SlashCmd('/bp %s %s' % (typeID, rest))
    821         else:
    822             return
    823 
    824     def cmd_createitem(self, p):
    825         qty = 1
    826         try:
    827             name, = p.Parse('s')
    828         except param.Error:
    829             try:
    830                 name, qty = p.Parse('ss')
    831             except param.Error:
    832                 try:
    833                     name, = p.Parse('r')
    834                 except param.Error:
    835                     return
    836 
    837         typeID = self.AutoComplete(name)
    838         if typeID:
    839             return sm.RemoteSvc('slash').SlashCmd('/createitem %s %s' % (typeID, qty))
    840         else:
    841             return
    842 
    843     def cmd_unload(self, p):
    844         try:
    845             target, rest = p.Parse('s?r')
    846             if rest is None:
    847                 rest = ''
    848             else:
    849                 rest = rest.lower().strip('"')
    850         except param.Error:
    851             return
    852 
    853         if target.lower() != 'me' or rest == 'all' or rest.isdigit():
    854             return
    855         matches = {}
    856         for rec in sm.GetService('invCache').GetInventoryFromId(session.shipid).List():
    857             if rec.categoryID != const.categoryOwner:
    858                 typeName = evetypes.GetName(rec.typeID)
    859                 if rest in typeName.lower():
    860                     matches[typeName] = rec.typeID
    861 
    862         if not matches:
    863             return
    864         matches = matches.items()
    865         matches.sort()
    866         if len(matches) > 1:
    867             ret = uix.ListWnd(matches, listtype='generic', caption='AutoComplete: %d types found' % len(matches))
    868         else:
    869             ret = matches[0]
    870         if ret:
    871             return sm.RemoteSvc('slash').SlashCmd('/unload me %s' % ret[1].typeID) or 'Ok'
    872         else:
    873             return
    874 
    875     def cmd_dogmaupdate(self, p):
    876         sm.RemoteSvc('slash').SlashCmd('/dogmaupdate')
    877         sm.GetService('clientEffectCompiler').effects.clear()
    878         prompt_player_fsd_dialog('CustomNotify', {'notify': 'Server has regenerated the expressions and yours have been cleared'})
    879         return 'Ok'
    880 
    881     def cmd_loadcontainer(self, p):
    882         return self.cmd_fit(p, cmd='load', categories=None)
    883 
    884     def cmd_fit(self, p, cmd = 'fit', categories = -1):
    885         if categories == -1:
    886             categories = [const.categoryModule, const.categoryDrone, const.categoryCharge]
    887         qty = ''
    888         try:
    889             target, name, qty = p.Parse('sss')
    890         except param.Error:
    891             try:
    892                 target, name = p.Parse('ss')
    893             except param.Error:
    894                 try:
    895                     target, name = p.Parse('sr')
    896                 except param.Error:
    897                     return
    898 
    899         typeID = self.AutoComplete(name, allowedCategories=categories)
    900         if typeID:
    901             return sm.RemoteSvc('slash').SlashCmd('/%s "%s" %s %s' % (cmd,
    902              target,
    903              typeID,
    904              qty))
    905         else:
    906             return
    907 
    908     def cmd_entity(self, p):
    909         qty = 1
    910         try:
    911             action, qty, name = p.Parse('sir')
    912         except param.Error:
    913             try:
    914                 action, name = p.Parse('sr')
    915             except param.Error:
    916                 return
    917 
    918         if action.lower() != 'deploy':
    919             return
    920         else:
    921             typeID = self.AutoComplete(name, allowedCategories=[const.categoryEntity])
    922             if typeID:
    923                 return sm.RemoteSvc('slash').SlashCmd('/entity deploy %s %s' % (qty, typeID)) or 'Ok'
    924             return
    925 
    926     def cmd_spawn(self, p):
    927         try:
    928             name, rest = p.Parse('sr')
    929         except param.Error:
    930             try:
    931                 name, = p.Parse('r')
    932                 rest = ''
    933             except param.Error:
    934                 return
    935 
    936         typeID = self.AutoComplete(name, allowedCategories=[const.categoryShip, const.categoryAsteroid, const.categoryDrone], allowedGroups=[const.groupLargeCollidableObject,
    937          const.groupCargoContainer,
    938          const.groupBiomass,
    939          const.groupComet,
    940          const.groupCloud,
    941          const.groupSentryGun,
    942          const.groupInvisibleBeacon])
    943         if typeID:
    944             return sm.RemoteSvc('slash').SlashCmd('/spawn %s %s' % (typeID, rest))
    945         else:
    946             return
    947 
    948     def cmd_spawnn(self, p):
    949         try:
    950             qty, deviation, name = p.Parse('ifr')
    951         except param.Error:
    952             return
    953 
    954         typeID = self.AutoComplete(name, allowedCategories=[const.categoryShip, const.categoryAsteroid, const.categoryDrone], allowedGroups=[const.groupLargeCollidableObject,
    955          const.groupCargoContainer,
    956          const.groupBiomass,
    957          const.groupComet,
    958          const.groupCloud,
    959          const.groupInvisibleBeacon,
    960          const.groupSentryGun])
    961         if typeID:
    962             return sm.RemoteSvc('slash').SlashCmd('/spawnn %s %s %s' % (qty, deviation, typeID)) or 'Ok'
    963         else:
    964             return
    965 
    966     def cmd_giveskills(self, p):
    967         try:
    968             target, skillname, level = p.Parse('ssi')
    969         except param.Error:
    970             try:
    971                 level = None
    972                 target, skillname = p.Parse('sr')
    973             except param.Error:
    974                 return
    975 
    976         skillname = skillname.strip('"')
    977         if skillname.lower() in ('all', 'super'):
    978             return
    979         else:
    980             typeID = self.AutoComplete(skillname, allowedCategories=[const.categorySkill])
    981             if typeID:
    982                 if level is None:
    983                     level = 5
    984                 return sm.RemoteSvc('slash').SlashCmd('/giveskill "%s" %s %s' % (target, typeID, level)) or 'Ok'
    985             return
    986 
    987     def cmd_removeskills(self, p):
    988         try:
    989             level = None
    990             target, skillname = p.Parse('sr')
    991         except param.Error:
    992             return
    993 
    994         skillname = skillname.strip('"')
    995         if skillname.lower() == 'all':
    996             return
    997         else:
    998             typeID = self.AutoComplete(skillname, allowedCategories=[const.categorySkill])
    999             if typeID:
   1000                 return sm.RemoteSvc('slash').SlashCmd('/removeskill "%s" %s' % (target, typeID)) or 'Ok'
   1001             return
   1002 
   1003     def cmd_skillscopy(self, p):
   1004         try:
   1005             fromCharacter, toCharacter = p.Parse('sr')
   1006         except param.Error:
   1007             return
   1008 
   1009         Progress('Preparing Copy', 'Please wait...', 0, 1)
   1010         try:
   1011             sm.RemoteSvc('slash').SlashCmd('/skillscopy "%s" "%s"' % (fromCharacter, toCharacter))
   1012         finally:
   1013             Progress('Copying Skills', 'Done!', 1, 1)
   1014 
   1015         return 'Ok'
   1016 
   1017     def _extract_windowName_from_params(self, p):
   1018         extracted_param = p.Parse('s')[0]
   1019         prefix = 'windowID=chatchannel_'
   1020         if extracted_param.startswith(prefix):
   1021             windowName = extracted_param[len(prefix):]
   1022         else:
   1023             raise Error("Couldn't extract a windowName from the params")
   1024         return windowName
   1025 
   1026     def cmd_massfleetinvite(self, p):
   1027         fleetSvc = sm.StartService('fleet')
   1028         fleetSvc.CheckIsInFleet()
   1029         windowName = self._extract_windowName_from_params(p)
   1030         candidates = sm.GetService('XmppChat').GetMemberIDsForNamedWindow(windowName)
   1031         if session.charid in candidates:
   1032             candidates.remove(session.charid)
   1033         t = len(candidates)
   1034         c = 0
   1035         for charID in candidates:
   1036             c += 1
   1037             Progress('Inviting...', '[%d/%d] %s' % (c, t, cfg.eveowners.Get(charID).ownerName), c, t)
   1038             try:
   1039                 fleetSvc.fleet.Invite(charID, None, None, None, True)
   1040             except:
   1041                 pass
   1042 
   1043         Progress('Inviting...', 'Done!', 1, 1)
   1044         return 'Ok'
   1045 
   1046     def cmd_masstransport(self, p):
   1047         windowName = self._extract_windowName_from_params(p)
   1048         memberIDs = sm.GetService('XmppChat').GetMemberIDsForNamedWindow(windowName)
   1049         local_memberIDs = sm.GetService('XmppChat').GetMemberIDsForNamedWindow('local')
   1050         candidates = [ charID for charID in memberIDs if charID not in local_memberIDs ]
   1051         if session.charid in candidates:
   1052             candidates.remove(session.charid)
   1053         t = len(candidates)
   1054         c = 0
   1055         for charID in candidates:
   1056             c += 1
   1057             Progress('Transfering...', '[%d/%d] %s' % (c, t, cfg.eveowners.Get(charID).ownerName), c, t)
   1058             sm.RemoteSvc('slash').SlashCmd('/tr %d me noblock' % charID)
   1059 
   1060         Progress('Transfering...', 'All commands sent to the server', 1, 1)
   1061         return 'Ok'
   1062 
   1063     def cmd_massstanding(self, p):
   1064         try:
   1065             fromID, newStanding = p.Parse('sr')
   1066         except:
   1067             raise UserError('SlashError', {'reason': 'Provide from and the new standing value.'})
   1068             sys.exc_clear()
   1069             return
   1070 
   1071         local = (('solarsystemid2', session.solarsystemid2),)
   1072         c = self.GetChannel()
   1073         if not c or c.channelID == local:
   1074             return
   1075         candidates = [ charID for charID in self.GetChannelUsers() ]
   1076         if session.charid in candidates:
   1077             candidates.remove(session.charid)
   1078         t = len(candidates)
   1079         c = 0
   1080         reason = 'Mass standing change by %s' % cfg.eveowners.Get(session.charid).name
   1081         corps = sm.RemoteSvc('lookupSvc').LookupCorporations(fromID)
   1082         corpIDs = [ each.corporationID for each in corps ]
   1083         factions = sm.RemoteSvc('lookupSvc').LookupFactions(fromID)
   1084         factionIDs = [ each.factionID for each in factions ]
   1085         resultIDs = corpIDs + factionIDs
   1086         resultList = [ (cfg.eveowners.Get(each).name, cfg.eveowners.Get(each).id, cfg.eveowners.Get(each).typeID) for each in resultIDs ]
   1087         if not resultList:
   1088             raise UserError('SlashError', {'reason': 'Unable to resolve corporation or faction name'})
   1089             sys.exc_clear()
   1090             return
   1091         ret = uix.ListWnd(resultList, caption='AutoComplete: %d types found' % len(resultList), ordered=1)
   1092         if ret:
   1093             if prompt_player_fsd_dialog('CustomQuestion', {'header': 'Change standings?',
   1094              'question': 'Do you really want to change the standings towards %s for the characters in this channel?' % ret[0]}, uiconst.YESNO) == uiconst.ID_YES:
   1095                 for charID in candidates:
   1096                     c += 1
   1097                     Progress('Setting standings...', '[%d/%d] %s' % (c, t, cfg.eveowners.Get(charID).ownerName), c, t)
   1098                     sm.RemoteSvc('slash').SlashCmd('/setstanding "%s" %d %s "%s"' % (ret[1],
   1099                      charID,
   1100                      newStanding,
   1101                      reason))
   1102 
   1103                 txt = 'Standings to %s set to %s for %d characters' % (ret[0], newStanding, c)
   1104             else:
   1105                 txt = 'Standings to %s left unmodified' % ret[0]
   1106         else:
   1107             txt = 'Standings left unmodified'
   1108         Progress('Setting standings...', 'Done!', 1, 1)
   1109         prompt_player_fsd_dialog('CustomNotify', {'notify': txt})
   1110         return 'Ok'
   1111 
   1112     def cmd_avatar(self, p):
   1113         b = u''
   1114         try:
   1115             a, b = p.Parse('sr')
   1116         except param.Error:
   1117             a = p.Parse('s')[0]
   1118 
   1119         if a.isdigit():
   1120             if not b:
   1121                 ad.DisplayCharacter(a)
   1122             else:
   1123                 b_params = b.split()
   1124                 dna_file = None
   1125                 anim_file = None
   1126                 audio_msg = None
   1127                 for par in b_params:
   1128                     if '.prs' in par:
   1129                         dna_file = par
   1130                     elif '.gr2' in par or '.gsf' in par:
   1131                         anim_file = par
   1132                     else:
   1133                         audio_msg = par
   1134 
   1135                 ad.DisplayCharacter(a, dna_str=dna_file, anim_str=anim_file, audio_str=audio_msg)
   1136         else:
   1137             a_params = a.split()
   1138             dna_file = None
   1139             anim_file = None
   1140             audio_msg = None
   1141             for par in a_params:
   1142                 if '.prs' in par:
   1143                     dna_file = par
   1144                 elif '.gr2' in par or '.gsf' in par:
   1145                     anim_file = par
   1146                 else:
   1147                     audio_msg = par
   1148 
   1149             if b:
   1150                 b_params = b.split()
   1151                 for par in b_params:
   1152                     if '.prs' in par:
   1153                         dna_file = par
   1154                     elif '.gr2' in par or '.gsf' in par:
   1155                         anim_file = par
   1156                     else:
   1157                         audio_msg = par
   1158 
   1159             ad.DisplayCharacter(0, dna_str=dna_file, anim_str=anim_file, audio_str=audio_msg)
   1160         return 'Ok'
   1161 
   1162     def cmd_avatarscene(self, p):
   1163         b = u''
   1164         anim_strs = []
   1165         audio_strs = []
   1166         b = p.Parse('r')
   1167         b_params = b[0].split()
   1168         a = b_params.pop(0)
   1169         n = None
   1170         if b_params:
   1171             for par in b_params:
   1172                 try:
   1173                     n = int(par)
   1174                 except ValueError():
   1175                     n = None
   1176                 except Exception as e:
   1177                     raise e
   1178 
   1179                 if '.gr2' in par or '.gsf' in par or par == 'no_anim':
   1180                     anim_strs.append(par)
   1181                 else:
   1182                     audio_strs.append(par)
   1183 
   1184         ad.DisplayPreparedScene(a, anim_strs=anim_strs, audio_strs=audio_strs, sub_char=n)
   1185         return 'Ok'
   1186 
   1187     def cmd_avatarcommand(self, p):
   1188         b = p.Parse('r')
   1189         b_params = b[0].split()
   1190         command = b_params.pop(0)
   1191         if b_params:
   1192             sm.ScatterEvent('OnAvatarCommand', command, *b_params)
   1193         else:
   1194             sm.ScatterEvent('OnAvatarCommand', command)
   1195         return 'Ok'
   1196 
   1197     def cmd_pos(self, p):
   1198         try:
   1199             action, id = p.Parse('sr')
   1200         except param.Error:
   1201             try:
   1202                 action = p.Parse('s')
   1203                 bp = sm.GetService('michelle').GetBallpark()
   1204                 for ballID in bp.balls.keys():
   1205                     try:
   1206                         item = bp.GetInvItem(ballID)
   1207                         if item.groupID in (const.groupControlTower,):
   1208                             id = item.itemID
   1209                             action = action[0]
   1210                     except:
   1211                         pass
   1212 
   1213             except:
   1214                 pass
   1215 
   1216         if not id:
   1217             return
   1218         elif action.lower() != 'fuel':
   1219             return
   1220         else:
   1221             try:
   1222                 id = int(id)
   1223             except ValueError:
   1224                 prompt_player_fsd_dialog('CustomInfo', {'info': "This must be called as either '/pos fuel' or '/pos fuel itemID'"})
   1225                 return 'ok'
   1226 
   1227             resourcesPerHour = []
   1228             reinforcedResourcesPerHour = []
   1229             totalFuelVolumePerCycle = float()
   1230             starbaseCharters = []
   1231             invCache = sm.GetService('invCache')
   1232             chosenTower = invCache.GetInventoryFromId(id)
   1233             secStatus = sm.GetService('map').GetSecurityStatus(session.locationid)
   1234             for typeID in evetypes.GetTypeIDsByGroup(const.groupLease):
   1235                 starbaseCharters.append(typeID)
   1236 
   1237             resources = get_control_tower_resources(chosenTower.typeID)
   1238             for resource in resources:
   1239                 if resource.resourceTypeID == const.typeStrontiumClathrates:
   1240                     reinforcedResourcesPerHour.append((resource.resourceTypeID, resource.quantity, evetypes.GetVolume(resource.resourceTypeID)))
   1241                 elif secStatus < 0.4:
   1242                     if resource.resourceTypeID not in starbaseCharters:
   1243                         resourcesPerHour.append((resource.resourceTypeID, resource.quantity, evetypes.GetVolume(resource.resourceTypeID)))
   1244                 else:
   1245                     resourcesPerHour.append((resource.resourceTypeID, resource.quantity, evetypes.GetVolume(resource.resourceTypeID)))
   1246 
   1247             for resourceTypeID, amountPerCycle, resourceVolume in resourcesPerHour:
   1248                 totalFuelVolumePerCycle += int(amountPerCycle * resourceVolume)
   1249 
   1250             totalFuelVolumePerCycle += 1
   1251             strontVolume = reinforcedResourcesPerHour[0][2]
   1252             fuelBay = chosenTower.GetCapacity()
   1253             strontBay = chosenTower.GetCapacity(flag=const.flagSecondaryStorage)
   1254             fuelCycles = int(fuelBay.capacity / totalFuelVolumePerCycle)
   1255             strontCycles = int(strontBay.capacity / strontVolume)
   1256             if session.role & ROLE_WORLDMOD:
   1257                 addList = []
   1258                 for commodity, amountPerCycle, resourceVolume in reinforcedResourcesPerHour:
   1259                     sm.GetService('slash').SlashCmd('crea %d %s' % (commodity, strontCycles))
   1260                     for cargo in invCache.GetInventoryFromId(session.shipid).ListCargo():
   1261                         if cargo.typeID == commodity:
   1262                             addList.append(cargo.itemID)
   1263 
   1264                 invCache.GetInventoryFromId(id).MultiAdd(addList, session.shipid, flag=const.flagSecondaryStorage)
   1265                 addList = []
   1266                 for fuel, amountPerCycle, resourceVolume in resourcesPerHour:
   1267                     sm.GetService('slash').SlashCmd('crea %d %s' % (fuel, int(fuelCycles * amountPerCycle)))
   1268                     for cargo in invCache.GetInventoryFromId(session.shipid).ListCargo():
   1269                         if cargo.typeID == fuel and cargo.stacksize == fuelCycles * amountPerCycle:
   1270                             addList.append(cargo.itemID)
   1271 
   1272                 invCache.GetInventoryFromId(id).MultiAdd(addList, session.shipid, flag=const.flagNone)
   1273                 return 'Ok'
   1274             shipType = const.typeBHMegaCargoShip
   1275             oldShipItemID = session.shipid
   1276             newShipItemID = int()
   1277             if invCache.GetInventoryFromId(int(session.shipid)).typeID != shipType:
   1278                 newShipItemID = sm.RemoteSvc('slash').SlashCmd('/spawn %d' % shipType)
   1279                 ship = sm.StartService('gameui').GetShipAccess()
   1280                 if ship:
   1281                     sm.StartService('sessionMgr').PerformSessionChange('board', ship.Board, newShipItemID, oldShipItemID)
   1282             blue.pyos.synchro.SleepSim(5000)
   1283             addList = []
   1284             for commodity, amountPerCycle, resourceVolume in reinforcedResourcesPerHour:
   1285                 sm.GetService('slash').SlashCmd('load me %d %s' % (commodity, strontCycles))
   1286                 for cargo in invCache.GetInventoryFromId(session.shipid).ListCargo():
   1287                     if cargo.typeID == commodity:
   1288                         addList.append(cargo.itemID)
   1289 
   1290             invCache.GetInventoryFromId(id).MultiAdd(addList, session.shipid, flag=const.flagSecondaryStorage)
   1291             addList = []
   1292             for fuel, amountPerCycle, resourceVolume in resourcesPerHour:
   1293                 sm.GetService('slash').SlashCmd('load me %d %s' % (fuel, int(fuelCycles * amountPerCycle)))
   1294                 for cargo in invCache.GetInventoryFromId(session.shipid).ListCargo():
   1295                     if cargo.typeID == fuel and cargo.stacksize == fuelCycles * amountPerCycle:
   1296                         addList.append(cargo.itemID)
   1297 
   1298             invCache.GetInventoryFromId(id).MultiAdd(addList, session.shipid, flag=const.flagNone)
   1299             if blue.os.GetSimTime() <= session.nextSessionChange:
   1300                 ms = 1000 + 1000 * (session.nextSessionChange - blue.os.GetSimTime()) / 10000000
   1301                 blue.pyos.synchro.SleepSim(ms)
   1302                 if newShipItemID:
   1303                     ship = sm.StartService('gameui').GetShipAccess()
   1304                     if ship:
   1305                         sm.StartService('sessionMgr').PerformSessionChange('board', ship.Board, oldShipItemID, newShipItemID)
   1306                         sm.GetService('slash').SlashCmd('heal %d 0' % newShipItemID)
   1307                         blue.pyos.synchro.SleepSim(5000)
   1308                         sm.GetService('insider').HealRemove(const.groupWreck)
   1309             return 'Ok'
   1310 
   1311     def GetAmmoTypesForWeapon(self, itemID):
   1312         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
   1313         dogmaItem = dogmaLocation.dogmaItems[itemID]
   1314         ammoInfoByTypeID = defaultdict(lambda : utillib.KeyVal(singletons=[], nonSingletons=[]))
   1315         validGroupIDs = dogmaLocation.dogmaStaticMgr.GetValidChargeGroupsForType(dogmaItem.typeID)
   1316         GetTypeAttribute = dogmaLocation.dogmaStaticMgr.GetTypeAttribute
   1317         preferredChargeSize = GetTypeAttribute(dogmaItem.typeID, const.attributeChargeSize)
   1318         legalTypes = set()
   1319         for typeID in evetypes.GetTypeIDsByGroups(validGroupIDs):
   1320             if preferredChargeSize is not None and GetTypeAttribute(typeID, const.attributeChargeSize) != preferredChargeSize:
   1321                 continue
   1322             legalTypes.add(typeID)
   1323 
   1324         return legalTypes
   1325 
   1326     def IsLegalAmmo(self, weaponItemID, chargeTypeID):
   1327         if not chargeTypeID:
   1328             return True
   1329         for typeID in self.GetAmmoTypesForWeapon(weaponItemID):
   1330             if typeID == chargeTypeID:
   1331                 return True
   1332 
   1333         return False
   1334 
   1335     def GetRandomAmmoForWeapon(self, weaponItemID):
   1336         for typeID in self.GetAmmoTypesForWeapon(weaponItemID):
   1337             return typeID
   1338 
   1339     def cmd_ammo(self, p):
   1340         try:
   1341             typeID = p.Parse('s')[0]
   1342         except:
   1343             raise UserError('SlashError', {'reason': 'Usage: /ammo [typeID]|random<br>Loads ammo of the specified type into all turrets or launchers that support this type.<br>If random is specified then all weapons are loaded with the first matching ammo found.'})
   1344 
   1345         if typeID == 'random':
   1346             typeID = 0
   1347         shipInv = sm.GetService('invCache').GetInventoryFromId(session.shipid)
   1348         for row in shipInv.ListHardwareModules():
   1349             if self.IsLegalAmmo(row.itemID, typeID):
   1350                 shipInv.ClearCharges(row.flagID)
   1351 
   1352         if typeID == 'clear':
   1353             return 'Ok'
   1354         typeID = int(typeID)
   1355         blue.pyos.synchro.SleepSim(1000)
   1356         cmds = []
   1357         stateMgr = sm.GetService('godma').GetStateManager()
   1358         for row in shipInv.ListHardwareModules():
   1359             thisTypeID = typeID
   1360             if not self.IsLegalAmmo(row.itemID, typeID):
   1361                 continue
   1362             if not thisTypeID:
   1363                 thisTypeID = self.GetRandomAmmoForWeapon(row.itemID)
   1364             if not thisTypeID:
   1365                 continue
   1366             self.LogNotice('/ammo: Putting %s into %s' % (thisTypeID, row.itemID))
   1367             ma = stateMgr.GetType(row.typeID)
   1368             num = int(ma.capacity / evetypes.GetVolume(thisTypeID))
   1369             if num > 0:
   1370                 cmds.append('/fit me %d %d flag=%d' % (thisTypeID, num, row.flagID))
   1371 
   1372         uthread.parallel([ (sm.GetService('slash').SlashCmd, (cmd,)) for cmd in cmds ])
   1373         return 'Ok'
   1374 
   1375     def cmd_sessionchangetimer(self, p):
   1376         oldVal = sessions.sessionChangeDelay
   1377         try:
   1378             secs = p.Parse('i')[0]
   1379             sessions.sessionChangeDelay = min(max(5, secs), 60) * const.SEC
   1380             session.nextSessionChange = None
   1381         except:
   1382             pass
   1383 
   1384         prompt_player_fsd_dialog('CustomNotify', {'notify': 'Session-Change Timer was changed from %ss to <b>%ss</b>.<br>This value will be reset next time you log in' % (oldVal / const.SEC, sessions.sessionChangeDelay / const.SEC)})
   1385         return 'Ok'
   1386 
   1387     def cmd_recustomize(self, p):
   1388         if sm.GetService('cc').NoExistingCustomization():
   1389             return 1
   1390         sm.GetService('gameui').GoCharacterCreationCurrentCharacter()
   1391         return 1
   1392 
   1393     def cmd_buyorders(self, p):
   1394         if session.stationid is None:
   1395             raise UserError('SlashError', {'reason': 'You must be in a station to execute this command.'})
   1396         qty = 1
   1397         orderRange = 32767
   1398         try:
   1399             name, = p.Parse('s')
   1400         except param.Error:
   1401             try:
   1402                 name, qty = p.Parse('si')
   1403             except param.Error:
   1404                 try:
   1405                     name, qty, orderRange = p.Parse('sii')
   1406                 except param.Error:
   1407                     return
   1408 
   1409         typeID = self.AutoComplete(name)
   1410         typeName = evetypes.GetName(typeID)
   1411         minVolume = 1
   1412         duration = 0
   1413         useCorp = False
   1414         qtyDone = 0
   1415         Progress('Buying %s %s' % (qty, typeName), 'Please wait...', 0, 1)
   1416         try:
   1417             while qty > qtyDone:
   1418                 order = sm.GetService('marketQuote').GetBestAskInRange(typeID, session.stationid, orderRange)
   1419                 if order is None:
   1420                     break
   1421                 if order.volRemaining + qtyDone > qty:
   1422                     buyQty = qty - qtyDone
   1423                 else:
   1424                     buyQty = order.volRemaining
   1425                 sm.GetService('marketQuote').BuyStuff(session.stationid, typeID, order.price, buyQty, orderRange, minVolume, duration, useCorp)
   1426                 blue.pyos.synchro.SleepSim(800)
   1427                 qtyDone += buyQty
   1428 
   1429         finally:
   1430             if qtyDone == qty:
   1431                 Progress('Done buying all the %s you asked me to.' % typeName, '', 1, 1)
   1432             else:
   1433                 Progress('Done. Did not find as many %s as you asked for.' % typeName, '', 1, 1)
   1434 
   1435         return 'Ok'
   1436 
   1437     def cmd_sellorders(self, p):
   1438         if session.stationid is None:
   1439             raise UserError('SlashError', {'reason': 'You must be in a station to execute this command.'})
   1440         qty = 1
   1441         orderCount = 1
   1442         priceFluct = 0
   1443         try:
   1444             theType, price = p.Parse('sf')
   1445         except param.Error:
   1446             try:
   1447                 theType, price, qty = p.Parse('sfi')
   1448             except param.Error:
   1449                 try:
   1450                     theType, price, qty, orderCount = p.Parse('sfii')
   1451                 except param.Error:
   1452                     try:
   1453                         theType, price, qty, orderCount, priceFluct = p.Parse('sfiif')
   1454                     except param.Error:
   1455                         raise UserError('SlashError', {'reason': 'Syntax error: should be /sellorders type price [qty] [ordercount] [price fluctuation in %]'})
   1456 
   1457         duration = 14
   1458         useCorp = False
   1459         officeID = None
   1460         expectedBrokersFee = None
   1461         if priceFluct != 0:
   1462             p = priceFluct / 100.0
   1463             minPriceCents = int(max(price - price * p, 1) * 100)
   1464             maxPriceCents = int((price + price * p) * 100)
   1465         Progress('Creating %s Market order(s)' % orderCount, 'Please wait...', 0, 1)
   1466         try:
   1467             validatedItems = []
   1468             itemID = self.cmd_createitem(param.ParamObject(str(theType) + ' ' + unicode(orderCount * qty)))
   1469             blue.pyos.synchro.SleepSim(1200)
   1470             typeID = sm.GetService('invCache').FetchItem(itemID, session.stationid).typeID
   1471             for x in xrange(orderCount):
   1472                 if priceFluct == 0:
   1473                     orderPrice = price
   1474                 else:
   1475                     orderPrice = random.randint(minPriceCents, maxPriceCents) / 100.0
   1476                 validatedItem = utillib.KeyVal(stationID=int(session.stationid), typeID=int(typeID), itemID=itemID, price=orderPrice, quantity=int(qty), officeID=officeID)
   1477                 sm.GetService('marketQuote').SellMulti([validatedItem], useCorp, duration, expectedBrokersFee)
   1478 
   1479         finally:
   1480             Progress('Done creating market orders.', '', 1, 1)
   1481 
   1482         return 'Ok'
   1483 
   1484     def cmd_cancelallorders(self, p):
   1485         orders = sm.GetService('marketQuote').GetMyOrders()
   1486         if not orders:
   1487             raise UserError('SlashError', {'reason': 'No market orders found.'})
   1488         for order in orders:
   1489             sm.GetService('marketQuote').CancelOrder(order.orderID, order.regionID)
   1490 
   1491         return 'Ok'
   1492 
   1493     def cmd_reimbursebounties(self, p):
   1494         sm.GetService('bountySvc').GMReimburseBounties()
   1495         return 'Ok'
   1496 
   1497     def cmd_clearbountycache(self, p):
   1498         sm.GetService('bountySvc').GMClearBountyCache()
   1499         return 'Ok'
   1500 
   1501     def cmd_killright(self, p):
   1502         sm.RemoteSvc('slash').SlashCmd('/killright %s' % p.line)
   1503         sm.GetService('bountySvc').ClearAllKillRightData()
   1504         return 'Ok'
   1505 
   1506     def cmd_newscanner(self, p):
   1507         cmd = p.Parse('s')
   1508         if 'start' in cmd:
   1509             sm.GetService('sensorSuite')
   1510         elif 'stop' in cmd:
   1511             sm.StopService('sensorSuite')
   1512         elif 'show' in cmd:
   1513             if session.solarsystemid is not None:
   1514                 sensorSuite = sm.GetService('sensorSuite')
   1515                 sensorSuite.EnableSensorOverlay()
   1516         elif 'hide' in cmd:
   1517             if session.solarsystemid is not None:
   1518                 sensorSuite = sm.GetService('sensorSuite')
   1519                 sensorSuite.EnableSensorOverlay()
   1520         elif 'register' in cmd:
   1521             if session.solarsystemid is not None:
   1522                 sensorSuite = sm.GetService('sensorSuite')
   1523                 sm.RemoteSvc('scanMgr').SignalTrackerRegister()
   1524         elif 'scan' in cmd:
   1525             if session.solarsystemid is not None:
   1526                 sensorSuite = sm.GetService('sensorSuite')
   1527                 sensorSuite.DisableSensorOverlay()
   1528                 sensorSuite.systemReadyTime = blue.os.GetSimTime() - const.SEC * 2
   1529                 sensorSuite.StartSensorSweep()
   1530         return 'Ok'
   1531 
   1532     def cmd_dplay(self, p):
   1533         coords = None
   1534         try:
   1535             dungeonID, godmode, x, y, z = p.Parse('i?i?f?f?f')
   1536             coords = (x, y, z)
   1537             if None in coords:
   1538                 coords = None
   1539         except param.Error:
   1540             raise Error('Use /dplay DUNGEONID [godmode:0|1]\n(godmode defaults to 1)')
   1541 
   1542         dungeonID = int(dungeonID)
   1543         if godmode is None:
   1544             godmode = 1
   1545         self.leveleditor = player = eveMoniker.Moniker('keeper', session.userid)
   1546         player.PlayDungeon(dungeonID, godmode=godmode, coords=coords)
   1547         return 'Ok'
   1548 
   1549     def cmd_dgoto(self, p):
   1550         roomID = int(p.Parse('i')[0])
   1551         self.leveleditor = player = eveMoniker.Moniker('keeper', session.userid)
   1552         player.GotoRoom(roomID)
   1553         return 'Ok'
   1554 
   1555     def cmd_highlight(self, p):
   1556         try:
   1557             cmd_option, _ = p.Parse('s?r')
   1558         except:
   1559             self._cmd_highlightError()
   1560 
   1561         if cmd_option == 'ui':
   1562             return self._cmd_highlightui(p)
   1563         if cmd_option == 'menu':
   1564             return self._cmd_highlightmenu(p)
   1565         if cmd_option == 'object':
   1566             return self._cmd_highlightobject(p)
   1567         if cmd_option == 'item':
   1568             return self._cmd_highlightitem(p)
   1569         if cmd_option == 'entitygroup':
   1570             return self._cmd_highlightentitygroup(p)
   1571         if cmd_option == 'type':
   1572             return self._cmd_highlighttype(p)
   1573         if cmd_option == 'group':
   1574             return self._cmd_highlightgroup(p)
   1575         if cmd_option == 'spaceobject':
   1576             return self._cmd_highlightspaceobject(p)
   1577         if cmd_option == 'clear':
   1578             return self._cmd_highlightclear(p)
   1579         self._cmd_highlightError()
   1580 
   1581     def _cmd_highlightError(self):
   1582         raise UserError('SlashError', {'reason': 'Available options:\n/highlight ui\n/highlight menu\n/highlight object\n/highlight item\n/highlight entitygroup\n/highlight type\n/highlight group\n/highlight spaceobject\n/highlight clear ui\n/highlight clear menu\n/highlight clear space\n/highlight clear all'})
   1583 
   1584     def _cmd_highlightui(self, p):
   1585         relativePositionString = None
   1586         style = None
   1587         try:
   1588             _, uiElementName, title, message, secondsTilFadeout, relativePositionString, style = p.Parse('ssssiss')
   1589         except:
   1590             try:
   1591                 _, uiElementName, title, message, secondsTilFadeout, relativePositionString = p.Parse('ssssis')
   1592             except:
   1593                 try:
   1594                     _, uiElementName, title, message, secondsTilFadeout = p.Parse('ssssi')
   1595                     relativePositionString = None
   1596                 except:
   1597                     try:
   1598                         _, uiElementName, title, message, relativePositionString = p.Parse('sssss')
   1599                         secondsTilFadeout = None
   1600                     except:
   1601                         try:
   1602                             _, uiElementName, title, message = p.Parse('ssss')
   1603                             relativePositionString = None
   1604                             secondsTilFadeout = None
   1605                         except:
   1606                             raise UserError('SlashError', {'reason': 'Usage: /highlight ui uiElementName title message [secondsTilFadeout] [relativePosition] [style]'})
   1607 
   1608         if relativePositionString and relativePositionString not in DIRECTION_STRING_TO_ID:
   1609             raise UserError('SlashError', {'reason': 'Available relative positions for UI highlight: left, up, right, down'})
   1610         if style and style not in UI_CLASS_BY_NAME:
   1611             raise UserError('SlashError', {'reason': 'Available styles for UI highlight: AuraBlue, RetroBlack'})
   1612         sm.GetService('uiHighlightingService').highlight_ui_element_by_name(ui_element_name=uiElementName, message=message, fadeout_seconds=secondsTilFadeout, title=title, default_direction=DIRECTION_STRING_TO_ID[relativePositionString] if relativePositionString else None, ui_class=style)
   1613         return 'Ok'
   1614 
   1615     def _cmd_highlightmenu(self, p):
   1616         try:
   1617             _, menu_name, type_id = p.Parse('ssi')
   1618         except:
   1619             try:
   1620                 _, menu_name = p.Parse('ss')
   1621                 type_id = None
   1622             except:
   1623                 raise UserError('SlashError', {'reason': 'Usage: /highlight menu menuName'})
   1624 
   1625         type_ids = [type_id] if type_id else []
   1626         sm.GetService('uiHighlightingService').highlight_menu_by_name(menu_name, type_ids)
   1627         return 'Ok'
   1628 
   1629     def _cmd_highlightobject(self, p):
   1630         try:
   1631             __, objectID, message, hint, secondsTilFadeout = p.Parse('sissi')
   1632             sm.GetService('uiHighlightingService').highlight_space_object_by_dungeon_object_id(objectID, message, hint, secondsTilFadeout)
   1633         except:
   1634             try:
   1635                 _, objectID, message, hint = p.Parse('siss')
   1636                 sm.GetService('uiHighlightingService').highlight_space_object_by_dungeon_object_id(objectID, message, hint)
   1637             except:
   1638                 raise UserError('SlashError', {'reason': 'Usage: /highlight object objectID message, hint, [secondsTilFadeout]'})
   1639 
   1640         return 'Ok'
   1641 
   1642     def _cmd_highlightitem(self, p):
   1643         try:
   1644             __, itemID, message, hint, secondsTilFadeout = p.Parse('sissi')
   1645             sm.GetService('uiHighlightingService').highlight_space_object_by_item_id(itemID, message, hint, secondsTilFadeout)
   1646         except:
   1647             try:
   1648                 _, itemID, message, hint = p.Parse('siss')
   1649                 sm.GetService('uiHighlightingService').highlight_space_object_by_item_id(itemID, message, hint)
   1650             except:
   1651                 raise UserError('SlashError', {'reason': 'Usage: /highlight item itemID message, hint, [secondsTilFadeout]'})
   1652 
   1653         return 'Ok'
   1654 
   1655     def _cmd_highlightentitygroup(self, p):
   1656         try:
   1657             __, entityGroupID, message, hint, secondsTilFadeout = p.Parse('sissi')
   1658             sm.GetService('uiHighlightingService').highlight_space_object_by_dungeon_entity_group_id(entityGroupID, message, hint, secondsTilFadeout)
   1659         except:
   1660             try:
   1661                 _, entityGroupID, message, hint = p.Parse('siss')
   1662                 sm.GetService('uiHighlightingService').highlight_space_object_by_dungeon_entity_group_id(entityGroupID, message, hint)
   1663             except:
   1664                 raise UserError('SlashError', {'reason': 'Usage: /highlight entitygroup entityGroupID message, hint, [secondsTilFadeout]'})
   1665 
   1666         return 'Ok'
   1667 
   1668     def _cmd_highlighttype(self, p):
   1669         try:
   1670             __, typeID, message, hint, secondsTilFadeout = p.Parse('sissi')
   1671             sm.GetService('uiHighlightingService').highlight_space_object_by_type(typeID, message, hint, secondsTilFadeout)
   1672         except:
   1673             try:
   1674                 _, typeID, message, hint = p.Parse('siss')
   1675                 sm.GetService('uiHighlightingService').highlight_space_object_by_type(typeID, message, hint)
   1676             except:
   1677                 raise UserError('SlashError', {'reason': 'Usage: /highlight type typeID message, hint, [secondsTilFadeout]'})
   1678 
   1679         return 'Ok'
   1680 
   1681     def _cmd_highlightgroup(self, p):
   1682         try:
   1683             _, groupID, message, hint, secondsTilFadeout = p.Parse('sissi')
   1684             sm.GetService('uiHighlightingService').highlight_space_object_by_group(groupID, message, hint, secondsTilFadeout)
   1685         except:
   1686             try:
   1687                 _, groupID, message, hint = p.Parse('siss')
   1688                 sm.GetService('uiHighlightingService').highlight_space_object_by_group(groupID, message, hint)
   1689             except:
   1690                 raise UserError('SlashError', {'reason': 'Usage: /highlight group groupID message, hint, [secondsTilFadeout]'})
   1691 
   1692         return 'Ok'
   1693 
   1694     def _cmd_highlightspaceobject(self, p):
   1695         try:
   1696             _, highlight_id = p.Parse('si')
   1697         except:
   1698             raise UserError('SlashError', {'reason': 'Usage: /highlight spaceobject highlight_id'})
   1699 
   1700         sm.GetService('uiHighlightingService').highlight_space_object(highlight_id)
   1701         return 'Ok'
   1702 
   1703     def _cmd_highlightclear(self, p):
   1704         cmd_option, clear_option = p.Parse('ss')
   1705         if clear_option == 'ui':
   1706             return self._cmd_highlightclearui(p)
   1707         if clear_option == 'menu':
   1708             return self._cmd_highlightclearmenu(p)
   1709         if clear_option == 'space':
   1710             return self._cmd_highlightclearspace(p)
   1711         if clear_option == 'all':
   1712             return self._cmd_highlightclearall(p)
   1713         self._cmd_highlightError()
   1714 
   1715     def _cmd_highlightclearui(self, p):
   1716         sm.GetService('uiHighlightingService').clear_ui_element_highlighting()
   1717         return 'Ok'
   1718 
   1719     def _cmd_highlightclearspace(self, p):
   1720         sm.GetService('uiHighlightingService').clear_space_object_highlighting()
   1721         return 'Ok'
   1722 
   1723     def _cmd_highlightclearmenu(self, p):
   1724         sm.GetService('uiHighlightingService').clear_menu_highlighting()
   1725         return 'Ok'
   1726 
   1727     def _cmd_highlightclearall(self, p):
   1728         sm.GetService('uiHighlightingService').clear_all_highlighting()
   1729         return 'Ok'
   1730 
   1731     def cmd_uiblink(self, p):
   1732         try:
   1733             cmd_option, _ = p.Parse('s?r')
   1734         except:
   1735             self._cmd_uiBlinkError()
   1736 
   1737         if cmd_option == 'start':
   1738             return self._cmd_uiblinkBlink(p)
   1739         if cmd_option == 'stop':
   1740             return self._cmd_uiblinkClear(p)
   1741         self._cmd_uiBlinkError()
   1742 
   1743     def _cmd_uiBlinkError(self):
   1744         raise UserError('SlashError', {'reason': 'Available options:\n/uiblink start {path}\n/uiblink stop {path}\n/uiblink stop all\n'})
   1745 
   1746     def _cmd_uiblinkBlink(self, p):
   1747         try:
   1748             cmd_option, ui_element_path = p.Parse('ss')
   1749         except Exception:
   1750             self._cmd_uiBlinkError()
   1751         else:
   1752             uiblinker.get_service().start_blinker_by_path(ui_element_path)
   1753             return 'OK'
   1754 
   1755     def _cmd_uiblinkClear(self, p):
   1756         try:
   1757             cmd_option, ui_element_path = p.Parse('ss')
   1758         except Exception:
   1759             self._cmd_uiBlinkError()
   1760         else:
   1761             if 'all' == ui_element_path:
   1762                 uiblinker.get_service().stop_all_blinkers()
   1763             else:
   1764                 uiblinker.get_service().stop_blinker_by_path(ui_element_path)
   1765 
   1766         return 'OK'
   1767 
   1768     def cmd_networth(self, p):
   1769         try:
   1770             cmd_option, _ = p.Parse('s?r')
   1771         except:
   1772             self._cmd_networthError()
   1773 
   1774         resultString = None
   1775         if cmd_option == 'total':
   1776             resultString = self._cmd_networthtotal()
   1777         elif cmd_option == 'wallet':
   1778             resultString = self._cmd_networthwallet()
   1779         elif cmd_option == 'assets':
   1780             resultString = self._cmd_networthassets()
   1781         elif cmd_option == 'contract':
   1782             resultString = self._cmd_networthcontract()
   1783         elif cmd_option == 'market':
   1784             resultString = self._cmd_networthmarket()
   1785         elif cmd_option == 'plex':
   1786             resultString = self._cmd_networthplex()
   1787         if resultString:
   1788             self._cmd_networthResults(resultString)
   1789             return 'Ok'
   1790         self._cmd_networthError()
   1791 
   1792     def _cmd_networthError(self):
   1793         raise UserError('SlashError', {'reason': 'Available options:\n/networth total\n/networth wallet\n/networth assets\n/networth plex\n/networth contract\n/networth market\n'})
   1794 
   1795     def _cmd_networthResults(self, string):
   1796         Message('Net Worth Results', string)
   1797 
   1798     def _cmd_networthtotal(self):
   1799         wallet = self._GetNetWorthWallet()
   1800         iskInContractEscrow, itemsInContractEscrow = self._GetNetWorthContracts()
   1801         iskInMarketEscrow, itemsInMarketEscrow = self._GetNetWorthMarket()
   1802         assets, assetsPlex = self._GetNetWorthAssets()
   1803         accountPlex = self._GetNetWorthAccountPlex()
   1804         combinedAssets = itemsInContractEscrow + itemsInMarketEscrow + assets
   1805         totalPlex = assetsPlex + accountPlex
   1806         total = wallet + iskInMarketEscrow + iskInContractEscrow + combinedAssets + totalPlex
   1807         return '<b>Total Net Worth:</b> {:0,.0f} ISK:<br/><b> - Wallet:</b> {:0,.0f} ISK<br/><b> - Combined assets:</b> {:0,.0f} ISK<br/><b> - PLEX:</b> {:0,.0f} ISK<br/><b> - Contracts:</b> {:0,.0f} ISK<br/><b> - Market orders:</b> {:0,.0f} ISK'.format(total, wallet, combinedAssets, totalPlex, iskInContractEscrow, iskInMarketEscrow)
   1808 
   1809     def _cmd_networthwallet(self):
   1810         wallet = self._GetNetWorthWallet()
   1811         return '<b>Wallet Balance:</b> {:0,.0f} ISK'.format(wallet)
   1812 
   1813     def _cmd_networthassets(self):
   1814         _, itemsInContractEscrow = self._GetNetWorthContracts()
   1815         _, itemsInMarketEscrow = self._GetNetWorthMarket()
   1816         assets, _ = self._GetNetWorthAssets()
   1817         total = itemsInContractEscrow + itemsInMarketEscrow + assets
   1818         return '<b>Combined Asset Net Worth:</b> {:0,.0f} ISK:<br/><b> - Assets:</b> {:0,.0f} ISK<br/><b> - Contracts:</b> {:0,.0f} ISK<br/><b> - Market orders:</b> {:0,.0f} ISK'.format(total, assets, itemsInContractEscrow, itemsInMarketEscrow)
   1819 
   1820     def _cmd_networthcontract(self):
   1821         iskInContractEscrow, _ = self._GetNetWorthContracts()
   1822         return '<b>Contract Escrow:</b> {:0,.0f} ISK'.format(iskInContractEscrow)
   1823 
   1824     def _cmd_networthmarket(self):
   1825         iskInMarketEscrow, _ = self._GetNetWorthMarket()
   1826         return '<b>Market Escrow:</b> {:0,.0f} ISK'.format(iskInMarketEscrow)
   1827 
   1828     def _cmd_networthplex(self):
   1829         _, assetsPlex = self._GetNetWorthAssets()
   1830         accountPlex = self._GetNetWorthAccountPlex()
   1831         totalPlex = assetsPlex + accountPlex
   1832         return '<b>PLEX Estimated Value:</b> {:0,.0f} ISK<br/><b>- PLEX in assets:</b> {:0,.0f} ISK<br/><b>- PLEX in Plex Vault:</b> {:0,.0f} ISK'.format(totalPlex, assetsPlex, accountPlex)
   1833 
   1834     def _GetNetWorthWallet(self):
   1835         return sm.GetService('wallet').GetWealth()
   1836 
   1837     def _GetNetWorthAssets(self):
   1838         invContainer = sm.GetService('invCache').GetInventory(const.containerGlobal)
   1839         assets, assetsPlex = invContainer.GetAssetWorth()
   1840         return (assets, assetsPlex)
   1841 
   1842     def _GetNetWorthMarket(self):
   1843         iskInMarketEscrow, itemsInMarketEscrow = sm.GetService('marketQuote').GetMyMarketEscrow()
   1844         return (iskInMarketEscrow, itemsInMarketEscrow)
   1845 
   1846     def _GetNetWorthContracts(self):
   1847         iskInContractEscrow, itemsInContractEscrow = sm.GetService('contracts').GetMyContractEscrow()
   1848         return (iskInContractEscrow, itemsInContractEscrow)
   1849 
   1850     def _GetNetWorthAccountPlex(self):
   1851         plex_amount = plex_api.get_repository().get_balance() or 0.0
   1852         plex_price = GetAveragePrice(typePlex) or 0.0
   1853         plex_worth = plex_amount * plex_price
   1854         return plex_worth
   1855 
   1856     def cmd_asteroid(self, p):
   1857         try:
   1858             cmd_option, _ = p.Parse('s?r')
   1859         except:
   1860             self._cmd_asteroidError()
   1861 
   1862         if cmd_option == 'highlight':
   1863             return self._cmd_asteroid_highlight(p)
   1864         if cmd_option == 'clear':
   1865             return self._cmd_asteroid_clear_highlight(p)
   1866         if cmd_option == 'rewards':
   1867             return self._cmd_asteroid_list_rewards()
   1868         self._cmd_asteroidError()
   1869 
   1870     def _cmd_asteroidError(self):
   1871         raise UserError('SlashError', {'reason': 'Available options:\n/asteroid rewards\n/asteroid highlight {asteroidID}\n/asteroid clear {asteroidID}'})
   1872 
   1873     def _cmd_asteroid_list_rewards(self):
   1874         sm.RemoteSvc('slash').SlashCmd('/asteroid rewards')
   1875         return 'Ok'
   1876 
   1877     def _cmd_asteroid_highlight(self, p):
   1878         try:
   1879             __, asteroidID = p.Parse('si')
   1880             sm.GetService('michelle').OnAsteroidRevealed(asteroidID)
   1881         except:
   1882             raise UserError('SlashError', {'reason': 'Usage: /asteroid highlight {asteroidID}'})
   1883 
   1884         return 'Ok'
   1885 
   1886     def _cmd_asteroid_clear_highlight(self, p):
   1887         try:
   1888             __, asteroidID = p.Parse('si')
   1889             sm.GetService('michelle').OnAsteroidTerminated(asteroidID)
   1890         except:
   1891             raise UserError('SlashError', {'reason': 'Usage: /asteroid clear {asteroidID}'})
   1892 
   1893         return 'Ok'
   1894 
   1895     def cmd_intro(self, p):
   1896         try:
   1897             cmd_option, _ = p.Parse('s?r')
   1898         except:
   1899             self._cmd_introError()
   1900             return
   1901 
   1902         if cmd_option in ('start', 'stop', 'terminate'):
   1903             return
   1904         self._cmd_introError()
   1905 
   1906     def _cmd_introError(self):
   1907         raise UserError('SlashError', {'reason': 'Available options:\n/intro start\n/intro stop\n/intro terminate\n/intro free'})
   1908 
   1909     def MatchTypes(self, name, allowedCategories = None, allowedGroups = None, smart = True):
   1910         name = name.strip('"')
   1911         self._PopulateTypeDict()
   1912 
   1913         def _filter(typeID):
   1914             if allowedCategories:
   1915                 if evetypes.GetCategoryID(typeID) in allowedCategories:
   1916                     return True
   1917                 if allowedGroups:
   1918                     if evetypes.GetGroupID(typeID) in allowedGroups:
   1919                         return True
   1920             elif allowedGroups:
   1921                 if evetypes.GetGroupID(typeID) in allowedGroups:
   1922                     return True
   1923             else:
   1924                 return True
   1925             return False
   1926 
   1927         if smart and name.lower() in self.typeIDsByName:
   1928             typeIDs = self.typeIDsByName[name.lower()]
   1929             return [ (evetypes.GetName(typeID), typeID) for typeID in typeIDs ]
   1930         if name.isdigit():
   1931             typeName = evetypes.GetNameOrNone(int(name))
   1932             if typeName:
   1933                 return [(typeName, int(name))]
   1934         else:
   1935             name = name.lower().strip('"')
   1936         if len(name) < 3:
   1937             raise UserError('SlashError', {'reason': 'Autocompletion requires 3 or more characters'})
   1938         matches = []
   1939         for typeName, typeIDs in self.typeIDsByName.iteritems():
   1940             if name in typeName:
   1941                 for typeID in typeIDs:
   1942                     if _filter(typeID):
   1943                         matches.append((evetypes.GetName(typeID), typeID))
   1944 
   1945         return matches
   1946 
   1947     def _PopulateTypeDict(self):
   1948         if self.typeIDsByName is not None:
   1949             return
   1950         d = defaultdict(list)
   1951         noOfExceptions = 0
   1952         optic_locale = localization.const.LOCALE_SHORT_CHINESE
   1953         for typeID in evetypes.Iterate():
   1954             blue.pyos.BeNice()
   1955             try:
   1956                 if boot.region == 'optic':
   1957                     if evetypes.GetNameID(typeID) is not None:
   1958                         d[evetypes.GetName(typeID, optic_locale).lower()].append(typeID)
   1959                 else:
   1960                     englishName = evetypes.GetEnglishName(typeID)
   1961                     if englishName is not None:
   1962                         d[englishName.lower()].append(typeID)
   1963             except Exception:
   1964                 if noOfExceptions == 0:
   1965                     log.LogException('Unexpected Exception in _PopulateTypeDict')
   1966                 noOfExceptions += 1
   1967                 sys.exc_clear()
   1968 
   1969         if noOfExceptions > 0:
   1970             self.LogError('_PopulateTypeDict failed', noOfExceptions, 'times getting the typeName')
   1971         self.typeIDsByName = d
   1972 
   1973     def AutoComplete(self, name, allowedCategories = None, allowedGroups = None):
   1974         matches = self.MatchTypes(name, allowedCategories, allowedGroups)
   1975         if not matches:
   1976             return None
   1977         if len(matches) == 1:
   1978             ret = matches[0]
   1979         else:
   1980             matches.sort()
   1981             ret = uix.ListWnd(matches, listtype='generic', caption='AutoComplete: %d types found' % len(matches))
   1982         if not ret:
   1983             raise Error('Cancelled')
   1984         return ret[1]
   1985 
   1986     def GetChannel(self):
   1987         log.LogTraceback('PAT: SlashService.GetChannel called')
   1988         f = sys._getframe().f_back
   1989         while f:
   1990             logger.debug('PAT: f=%s', f)
   1991             loc = f.f_locals
   1992             logger.debug('PAT: loc=%s', loc)
   1993             if 'self' in loc:
   1994                 target = loc['self']
   1995                 logger.debug('PAT: target=%s', target)
   1996                 from packages.xmppchatclient.xmppchatwindow import XmppChatWindow
   1997                 if isinstance(target, XmppChatWindow):
   1998                     logger.debug('PAT: Found XmppChatWindow target instance: %s', target)
   1999                     return target
   2000             f = f.f_back
   2001 
   2002         raise Error('This function can only be used in a chat channel')
   2003 
   2004     def GetChannelUsers(self):
   2005         c = self.GetChannel()
   2006         return [ each.charID for each in c.userlist.sr.nodes ]
   2007 
   2008     def ReportSlashCommandUsage(self, text):
   2009         splitText = text.split(' ', 1)
   2010         commandText = splitText[0].lower()
   2011         argumentText = splitText[1] if len(splitText) > 1 else ''
   2012         self._slashEventPublisher.command_used(commandText, argumentText)
   2013 
   2014 
   2015 exports = {'slash.Error': Error}