def func(self): "Implement the command" caller = self.caller if self.args and self.args.isdigit(): nlim = int(self.args) else: nlim = 10 nobjs = ObjectDB.objects.count() base_char_typeclass = settings.BASE_CHARACTER_TYPECLASS nchars = ObjectDB.objects.filter( db_typeclass_path=base_char_typeclass).count() nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude( db_typeclass_path=base_char_typeclass).count() nexits = ObjectDB.objects.filter(db_location__isnull=False, db_destination__isnull=False).count() nother = nobjs - nchars - nrooms - nexits nobjs = nobjs or 1 # fix zero-div error with empty database # total object sum table totaltable = EvTable("{wtype{n", "{wcomment{n", "{wcount{n", "{w%%{n", border="table", align="l") totaltable.align = 'l' totaltable.add_row("Characters", "(BASE_CHARACTER_TYPECLASS)", nchars, "%.2f" % ((float(nchars) / nobjs) * 100)) totaltable.add_row("Rooms", "(location=None)", nrooms, "%.2f" % ((float(nrooms) / nobjs) * 100)) totaltable.add_row("Exits", "(destination!=None)", nexits, "%.2f" % ((float(nexits) / nobjs) * 100)) totaltable.add_row("Other", "", nother, "%.2f" % ((float(nother) / nobjs) * 100)) # typeclass table typetable = EvTable("{wtypeclass{n", "{wcount{n", "{w%%{n", border="table", align="l") typetable.align = 'l' dbtotals = ObjectDB.objects.object_totals() for path, count in dbtotals.items(): typetable.add_row(path, count, "%.2f" % ((float(count) / nobjs) * 100)) # last N table objs = ObjectDB.objects.all().order_by( "db_date_created")[max(0, nobjs - nlim):] latesttable = EvTable("{wcreated{n", "{wdbref{n", "{wname{n", "{wtypeclass{n", align="l", border="table") latesttable.align = 'l' for obj in objs: latesttable.add_row(utils.datetime_format(obj.date_created), obj.dbref, obj.key, obj.path) string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % ( nobjs, totaltable) string += "\n{wObject typeclass distribution:{n\n%s" % typetable string += "\n{wLast %s Objects created:{n\n%s" % (min( nobjs, nlim), latesttable) caller.msg(string)
def count_poses(self): """Makes a board post of characters with insufficient pose-counts""" qs = ObjectDB.objects.filter(roster__roster__name="Active") min_poses = 20 low_activity = [] for ob in qs: if (ob.posecount < min_poses and (ob.tags.get("rostercg") and ob.player_ob and not ob.player_ob.tags.get("staff_alt"))): low_activity.append(ob) ob.db.previous_posecount = ob.posecount ob.posecount = 0 board = BBoard.objects.get(db_key__iexact="staff") table = EvTable("{wName{n", "{wNum Poses{n", border="cells", width=78) for ob in low_activity: table.add_row(ob.key, ob.db.previous_posecount) board.bb_post(poster_obj=self, msg=str(table), subject="Inactive by Poses List") # Various 'Beats' ------------------------------------------------- #def process_journals(self, player): """ In the journals here, we're processing all the XP gained for making journals, comments, or updating relationships. """ char = player.char_ob try: account = player.roster.current_account if account.id in self.ndb.xptypes: total = self.ndb.xptypes[account.id].get("journals", 0) else: self.ndb.xptypes[account.id] = {} total = 0 journal_total = char.messages.num_weekly_journals xp = 0 if journal_total > 0: xp += 4 if journal_total > 1: xp += 2 if journal_total > 2: xp += 1 # XP capped at 7 for all sources if xp > 7: xp = 7 if xp + total > 7: xp = 7 - total if xp <= 0: return except (ValueError, TypeError): return except AttributeError: return except Exception as err: print("ERROR in process journals: %s" % err) traceback.print_exc() return if xp: msg = "You received %s xp this week for journals/comments/relationship updates." % xp self.award_xp(char, xp, player, msg, xptype="journals") # ----------------------------------------------------------------- #def count_votes(self, player): """ Counts the votes for each player. We may log voting patterns later if we need to track against abuse, but since voting is stored in each player it's fairly trivial to check each week on an individual basis anyway. """ votes = player.db.votes or [] for ob in votes: self.ndb.recorded_votes[ob] += 1 if votes: self.ndb.vote_history[player] = votes
def post_top_prestige(self): """Makes a board post of the top prestige earners this past week""" import random from world.dominion.models import PraiseOrCondemn changes = PraiseOrCondemn.objects.filter(week=self.db.week).exclude( target__organization_owner__secret=True) praises = defaultdict(list) condemns = defaultdict(list) total_values = {} for praise in changes.filter(value__gte=0): praises[praise.target].append(praise) for condemn in changes.filter(value__lt=0): condemns[condemn.target].append(condemn) for change in changes: current = total_values.get(change.target, 0) current += change.value total_values[change.target] = current board = BBoard.objects.get(db_key__iexact=PRESTIGE_BOARD_NAME) def get_total_from_list(entry_list): """Helper function to get total prestige amount from a list""" return sum(praise_ob.value for praise_ob in entry_list) sorted_praises = sorted(praises.items(), key=lambda x: get_total_from_list(x[1]), reverse=True) sorted_praises = sorted_praises[:20] table = EvTable("{wName{n", "{wValue{n", "{wMsg{n", border="cells", width=78) for tup in sorted_praises: praise_messages = [ob.message for ob in tup[1] if ob.message] selected_message = "" if praise_messages: selected_message = random.choice(praise_messages) table.add_row( str(tup[0]).capitalize()[:18], get_total_from_list(tup[1]), selected_message) table.reformat_column(0, width=18) table.reformat_column(1, width=10) table.reformat_column(2, width=50) prestige_msg = "{wMost Praised this week{n".center(72) prestige_msg = "%s\n%s" % (prestige_msg, str(table).lstrip()) prestige_msg += "\n\n" try: # sort by our prestige change amount sorted_changes = sorted(total_values.items(), key=lambda x: abs(x[1]), reverse=True) sorted_changes = sorted_changes[:20] table = EvTable("{wName{n", "{wPrestige Change Amount{n", "{wPrestige Rank{n", border="cells", width=78) rank_order = list( AssetOwner.objects.filter( player__player__roster__roster__name="Active").distinct()) rank_order = sorted(rank_order, key=lambda x: x.prestige, reverse=True) for tup in sorted_changes: # get our prestige ranking compared to others owner = tup[0] try: rank = rank_order.index(owner) + 1 except ValueError: # they rostered mid-week or whatever, skip them continue # get the amount that our prestige has changed. add + for positive amt = tup[1] if amt > 0: amt = "+%s" % amt table.add_row(owner, amt, rank) prestige_msg += "\n\n" prestige_msg += "{wTop Prestige Changes{n".center(72) prestige_msg = "%s\n%s" % (prestige_msg, str(table).lstrip()) except (AttributeError, ValueError, TypeError): import traceback traceback.print_exc() board.bb_post(poster_obj=self, msg=prestige_msg, subject="Weekly Praises/Condemns", poster_name="Prestige") inform_staff("Praises/condemns tally complete. Posted on %s." % board)
def list_callbacks(self): """Display the list of callbacks connected to the object.""" obj = self.obj callback_name = self.callback_name parameters = self.parameters callbacks = self.handler.get_callbacks(obj) types = self.handler.get_events(obj) if callback_name: # Check that the callback name can be found in this object created = callbacks.get(callback_name) if created is None: self.msg("No callback {} has been set on {}.".format( callback_name, obj)) return if parameters: # Check that the parameter points to an existing callback try: number = int(parameters) - 1 assert number >= 0 callback = callbacks[callback_name][number] except (ValueError, AssertionError, IndexError): self.msg( "The callback {} {} cannot be found in {}.".format( callback_name, parameters, obj)) return # Display the callback's details author = callback.get("author") author = author.key if author else "|gUnknown|n" updated_by = callback.get("updated_by") updated_by = updated_by.key if updated_by else "|gUnknown|n" created_on = callback.get("created_on") created_on = created_on.strftime( "%Y-%m-%d %H:%M:%S") if created_on else "|gUnknown|n" updated_on = callback.get("updated_on") updated_on = updated_on.strftime( "%Y-%m-%d %H:%M:%S") if updated_on else "|gUnknown|n" msg = "Callback {} {} of {}:".format(callback_name, parameters, obj) msg += "\nCreated by {} on {}.".format(author, created_on) msg += "\nUpdated by {} on {}".format(updated_by, updated_on) if self.is_validator: if callback.get("valid"): msg += "\nThis callback is |rconnected|n and active." else: msg += "\nThis callback |rhasn't been|n accepted yet." msg += "\nCallback code:\n" msg += raw(callback["code"]) self.msg(msg) return # No parameter has been specified, display the table of callbacks cols = ["Number", "Author", "Updated", "Param"] if self.is_validator: cols.append("Valid") table = EvTable(*cols, width=78) table.reformat_column(0, align="r") now = datetime.now() for i, callback in enumerate(created): author = callback.get("author") author = author.key if author else "|gUnknown|n" updated_on = callback.get("updated_on") if updated_on is None: updated_on = callback.get("created_on") if updated_on: updated_on = "{} ago".format( time_format((now - updated_on).total_seconds(), 4).capitalize()) else: updated_on = "|gUnknown|n" parameters = callback.get("parameters", "") row = [str(i + 1), author, updated_on, parameters] if self.is_validator: row.append("Yes" if callback.get("valid") else "No") table.add_row(*row) self.msg(unicode(table)) else: names = list(set(list(types.keys()) + list(callbacks.keys()))) table = EvTable("Callback name", "Number", "Description", valign="t", width=78) table.reformat_column(0, width=20) table.reformat_column(1, width=10, align="r") table.reformat_column(2, width=48) for name in sorted(names): number = len(callbacks.get(name, [])) lines = sum( len(e["code"].splitlines()) for e in callbacks.get(name, [])) no = "{} ({})".format(number, lines) description = types.get(name, (None, "Chained event."))[1] description = description.strip("\n").splitlines()[0] table.add_row(name, no, description) self.msg(unicode(table))
def func(self): caller = self.caller args = self.args.strip() swap = any(s.startswith('s') for s in self.switches) if args: if hasattr(self, "item"): obj = self.item del self.item else: obj = caller.search( args, candidates=caller.contents, nofound_string=_INVENTORY_ERRMSG.format(args)) if obj: if hasattr(self, "action"): action = self.action del self.action else: if any(isinstance(obj, i) for i in (Weapon, Shield)): action = 'wield' elif any( isinstance(obj, i) for i in (Torso, Helm, Boots, Gloves, Necklace, Bracers, Belt, Ring)): action = 'wear' else: caller.msg("You can't equip {}.".format( obj.get_display_name(caller))) if not obj.access(caller, 'equip'): caller.msg("You can't {} {}.".format( action, obj.get_display_name(caller))) return if obj in caller.equip: caller.msg("You're already {}ing {}.".format( action, obj.get_display_name(caller))) return # check whether slots are occupied occupied_slots = [ caller.equip.get(s) for s in obj.db.slots if caller.equip.get(s) ] if obj.db.multi_slot: if len(occupied_slots) > 0: if swap: for item in occupied_slots: caller.equip.remove(item) else: caller.msg("You can't {} {}. ".format( action, obj.get_display_name(caller)) + "You already have something there.") return else: if len(occupied_slots) == len(obj.db.slots): if swap: caller.equip.remove(occupied_slots[0]) else: caller.msg("You can't {} {}. ".format( action, obj.get_display_name(caller)) + "You have no open {} slot{}.".format( ", or ".join(obj.db.slots), "s" if len(obj.db.slots) != 1 else "")) return if not caller.equip.add(obj): caller.msg("You can't {} {}.".format( action, obj.get_display_name(caller))) return # call hook if hasattr(obj, "at_equip"): obj.at_equip(caller) caller.msg("You {} {}.".format(action, obj.get_display_name(caller))) caller.location.msg_contents("{actor} {action}s {obj}.", mapping=dict(actor=caller, obj=obj, action=action), exclude=caller) else: # no arguments; display current equip data = [] s_width = max(len(s) for s in caller.equip.slots) for slot, item in caller.equip: if not item or not item.access(caller, 'view'): continue stat = " " if item.attributes.has('damage_roll'): stat += "(|rDamage: {:>2d}|n) ".format(item.db.damage_roll) if item.attributes.has('physical_bonus'): stat += "(|yPhysical bonus: {:>2d}|n)".format( item.db.physical_bonus) if item.attributes.has('magical_bonus'): stat += "(|yMagical bonus: {:>2d}|n)".format( item.db.magical_bonus) if item.attributes.has('range'): stat += "(|G{}|n) ".format(item.db.range.capitalize()) data.append( " |b{slot:>{swidth}.{swidth}}|n: {item:<20.20} {stat}". format( slot=slot.capitalize(), swidth=s_width, item=item.name, stat=stat, )) if len(data) <= 0: output = "You have nothing in your equipment." else: table = EvTable(header=False, border=None, table=[data]) output = "|YYour equipment:|n\n{}".format(table) caller.msg(output)
def func(self): """Show server time data in a table.""" lat, lon, ele = 33.43, -112.07, 24 here = self.character.location if here: x = float(here.tags.get(category="coordx", default=0)) y = float(here.tags.get(category="coordy", default=0)) # z = here.tags.get(category="coordz") if x and y: lat, lon = float(y / 10000), float(x / 10000) print('Using location coordinates: {}, {}'.format(lat, lon)) place = Astral.Location(info=('', '', lat, lon, 'UTC', ele)) place.solar_depression = 'civil' def time_dif(at, when): diff = abs(at - when) return 'now' if diff.total_seconds < 60 else ( utils.time_format(diff.total_seconds(), 2) + (' ago' if at > when else '')) def moon_phase(days): """ Summarize the visible portion, given days into cycle Args: days (int or float): days into current cycle Returns: phase (str): summary of view of visible portion """ phases = ('new', 'waxing crescent', 'First quarter', 'waxing gibbous', 'full', 'waning gibbous', 'last quarter', 'waning crescent') percent = float((float(days) + 0.5) / 29.53) phase = int((percent - int(percent)) * len(phases)) return phases[phase] try: sun = place.sun(date=datetime.date.today(), local=True) except Exception: return else: past = here.tags.get('past', category='realm') moon = place.moon_phase(date=datetime.date.today()) now = timezone.now() moment = ['dawn', 'sunrise', 'noon', 'sunset', 'dusk'] events = zip([each.capitalize() + ':' for each in moment], [time_dif(now, sun[each]) for each in moment]) table1 = EvTable("|wServer", '|wtime', align='l', width=75) table1.add_row('Current uptime', utils.time_format(gametime.uptime(), 3)) table1.add_row( 'First start', time_dif( datetime.datetime.now(), datetime.datetime.fromtimestamp(gametime.server_epoch()))) if here.tags.get('past', category='realm'): table1.add_row('Moon phase', moon_phase(moon)) table1.reformat_column(0, width=20) if past: table2 = EvTable("|wEvent", "|wTime until", align="l", width=75) for entry in events: table2.add_row(entry[0], entry[1]) table2.reformat_column(0, width=20) if self.cmdstring == 'uptime': self.msg('Current uptime: ' + utils.time_format(gametime.uptime(), 3)) else: self.msg(unicode(table1)) if past: if self.cmdstring == 'events': self.msg(unicode(table2)) else: self.msg("\n" + unicode(table2))
def func(self): """Show list.""" global _IDMAPPER if not _IDMAPPER: from evennia.utils.idmapper import models as _IDMAPPER if "flushmem" in self.switches: # flush the cache prev, _ = _IDMAPPER.cache_size() nflushed = _IDMAPPER.flush_cache() now, _ = _IDMAPPER.cache_size() string = "The Idmapper cache freed |w{idmapper}|n database objects.\n" \ "The Python garbage collector freed |w{gc}|n Python instances total." self.caller.msg(string.format(idmapper=(prev - now), gc=nflushed)) return # display active processes os_windows = os.name == "nt" pid = os.getpid() if os_windows: # Windows requires the psutil module to even get paltry # statistics like this (it's pretty much worthless, # unfortunately, since it's not specific to the process) /rant try: import psutil has_psutil = True except ImportError: has_psutil = False if has_psutil: loadavg = psutil.cpu_percent() _mem = psutil.virtual_memory() rmem = _mem.used / (1000.0 * 1000) pmem = _mem.percent if "mem" in self.switches: string = "Total computer memory usage: |w%g|n MB (%g%%)" self.caller.msg(string % (rmem, pmem)) return # Display table loadtable = EvTable("property", "statistic", align="l") loadtable.add_row("Total CPU load", "%g %%" % loadavg) loadtable.add_row("Total computer memory usage", "%g MB (%g%%)" % (rmem, pmem)) loadtable.add_row("Process ID", "%g" % pid), else: loadtable = "Not available on Windows without 'psutil' library " \ "(install with |wpip install psutil|n)." else: # Linux / BSD (OSX) - proper pid-based statistics global _RESOURCE if not _RESOURCE: import resource as _RESOURCE loadavg = os.getloadavg()[0] rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1000.0 # resident memory vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1000.0 # virtual memory pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read()) # % of resident memory to total rusage = _RESOURCE.getrusage(_RESOURCE.RUSAGE_SELF) if "mem" in self.switches: string = "Memory usage: RMEM: |w%g|n MB (%g%%), VMEM (res+swap+cache): |w%g|n MB." self.caller.msg(string % (rmem, pmem, vmem)) return loadtable = EvTable("property", "statistic", align="l") loadtable.add_row("Server load (1 min)", "%g" % loadavg) loadtable.add_row("Process ID", "%g" % pid), loadtable.add_row("Memory usage", "%g MB (%g%%)" % (rmem, pmem)) loadtable.add_row("Virtual address space", "") loadtable.add_row("|x(resident+swap+caching)|n", "%g MB" % vmem) loadtable.add_row("CPU time used (total)", "%s (%gs)" % (utils.time_format(rusage.ru_utime), rusage.ru_utime)) loadtable.add_row("CPU time used (user)", "%s (%gs)" % (utils.time_format(rusage.ru_stime), rusage.ru_stime)) loadtable.add_row("Page faults", "%g hard, %g soft, %g swapouts" % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap)) loadtable.add_row("Disk I/O", "%g reads, %g writes" % (rusage.ru_inblock, rusage.ru_oublock)) loadtable.add_row("Network I/O", "%g in, %g out" % (rusage.ru_msgrcv, rusage.ru_msgsnd)) loadtable.add_row("Context switching", "%g vol, %g forced, %g signals" % (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals)) # os-generic string = "|wServer CPU and Memory load:|n\n%s" % loadtable # object cache count (note that sys.getsiseof is not called so this works for pypy too. total_num, cachedict = _IDMAPPER.cache_size() sorted_cache = sorted([(key, num) for key, num in cachedict.items() if num > 0], key=lambda tup: tup[1], reverse=True) memtable = EvTable("entity name", "number", "idmapper %", align="l") for tup in sorted_cache: memtable.add_row(tup[0], "%i" % tup[1], "%.2f" % (float(tup[1]) / total_num * 100)) string += "\n|w Entity idmapper cache:|n %i items\n%s" % (total_num, memtable) # return to caller self.caller.msg(string)
def func(self): caller = self.caller if not self.args: self._usage() return # split the target off from the first list argument targs = tuple(self.lhslist[0].rsplit(' ', 1)) if len(targs) == 1 or \ (len(targs) > 1 and targs[1].lower() not in ALL_SKILLS): target, self.lhslist[0] = self.lhslist[0], '' else: target, self.lhslist[0] = targs char = caller.search(target, location=caller.location, typeclass='typeclasses.characters.NPC') if not char: return # the inherits_from check below is necessary due to an issue # with search() on the ContripRPObject class if not inherits_from(char, 'typeclasses.characters.NPC'): caller.msg("Could not find NPC: '{}'".format(target)) return if self.rhs: # handle assignments if not all((x.isdigit() for x in self.rhslist)): caller.msg('Assignment values must be numeric.') return if len(self.lhslist) != len(self.rhslist): caller.msg('Incorrect number of assignment values.') return for i in xrange(len(self.lhslist)): if self.lhslist[i].lower() in char.skills.all: char.skills[self.lhslist[i].lower()].base = \ min(max(int(self.rhslist[i]), 0), 10) # enforce {0, 10} bounds caller.msg('Skill "{}" set to {} for {}'.format( self.lhslist[i].lower(), min(max(int(self.rhslist[i]), 0), 10), char.sdesc.get())) else: caller.msg('Invalid skill: "{}"'.format(self.lhslist[i])) # display traits data = [] if any(self.lhslist): skills = [s.lower() for s in self.lhslist if s.lower() in char.skills.all] else: skills = char.skills.all if len(skills) < 3: [data.append([self._format_skill_3col(char.skills[s])]) for s in skills] else: [data.append([self._format_skill_3col(char.skills[s]) for s in skills[i::3]]) for i in xrange(3)] table = EvTable(header=False, table=data) caller.msg(unicode(table))
def _parse_rectangles(self, cellchar, tablechar, form, **kwargs): """ Parse a form for rectangular formfields identified by formchar enclosing an identifier. """ # update options given at creation with new input - this # allows e.g. self.map() to add custom settings for individual # cells/tables custom_options = copy.copy(self.options) custom_options.update(kwargs) nform = len(form) mapping = {} cell_coords = {} table_coords = {} # Locate the identifier tags and the horizontal end coords for all forms re_cellchar = re.compile( r"%s+([^%s%s])%s+" % (cellchar, INVALID_FORMCHARS, cellchar, cellchar)) re_tablechar = re.compile( r"%s+([^%s%s|])%s+" % (tablechar, INVALID_FORMCHARS, tablechar, tablechar)) for iy, line in enumerate(_to_ansi(form, regexable=True)): # find cells ix0 = 0 while True: match = re_cellchar.search(line, ix0) if match: # get the width of the rectangle directly from the match cell_coords[match.group(1)] = [ iy, match.start(), match.end() ] ix0 = match.end() else: break # find tables ix0 = 0 while True: match = re_tablechar.search(line, ix0) if match: # get the width of the rectangle directly from the match table_coords[match.group(1)] = [ iy, match.start(), match.end() ] ix0 = match.end() else: break #print "cell_coords:", cell_coords #print "table_coords:", table_coords # get rectangles and assign EvCells for key, (iy, leftix, rightix) in cell_coords.items(): # scan up to find top of rectangle dy_up = 0 if iy > 0: for i in range(1, iy): #print "dy_up:", [form[iy-i][ix] for ix in range(leftix, rightix)] if all(form[iy - i][ix] == cellchar for ix in range(leftix, rightix)): dy_up += 1 else: break # find bottom edge of rectangle dy_down = 0 if iy < nform - 1: for i in range(1, nform - iy - 1): #print "dy_down:", [form[iy+i][ix]for ix in range(leftix, rightix)] if all(form[iy + i][ix] == cellchar for ix in range(leftix, rightix)): dy_down += 1 else: break # we have our rectangle. Calculate size of EvCell. iyup = iy - dy_up iydown = iy + dy_down width = rightix - leftix height = abs(iyup - iydown) + 1 # we have all the coordinates we need. Create EvCell. data = self.cells_mapping.get(key, "") #if key == "1": # print "creating cell '%s' (%s):" % (key, data) # print "iy=%s, iyup=%s, iydown=%s, leftix=%s, rightix=%s, width=%s, height=%s" % (iy, iyup, iydown, leftix, rightix, width, height) options = { "pad_left": 0, "pad_right": 0, "pad_top": 0, "pad_bottom": 0, "align": "l", "valign": "t", "enforce_size": True } options.update(custom_options) #if key=="4": #print "options:", options mapping[key] = (iyup, leftix, width, height, EvCell(data, width=width, height=height, **options)) # get rectangles and assign Tables for key, (iy, leftix, rightix) in table_coords.items(): # scan up to find top of rectangle dy_up = 0 if iy > 0: for i in range(1, iy): #print "dy_up:", [form[iy-i][ix] for ix in range(leftix, rightix)] if all(form[iy - i][ix] == tablechar for ix in range(leftix, rightix)): dy_up += 1 else: break # find bottom edge of rectangle dy_down = 0 if iy < nform - 1: for i in range(1, nform - iy - 1): #print "dy_down:", [form[iy+i][ix]for ix in range(leftix, rightix)] if all(form[iy + i][ix] == tablechar for ix in range(leftix, rightix)): dy_down += 1 else: break # we have our rectangle. Calculate size of Table. iyup = iy - dy_up iydown = iy + dy_down width = rightix - leftix height = abs(iyup - iydown) + 1 # we have all the coordinates we need. Create Table. table = self.tables_mapping.get(key, None) #print "creating table '%s' (%s):" % (key, data) #print "iy=%s, iyup=%s, iydown=%s, leftix=%s, rightix=%s, width=%s, height=%s" % (iy, iyup, iydown, leftix, rightix, width, height) options = { "pad_left": 0, "pad_right": 0, "pad_top": 0, "pad_bottom": 0, "align": "l", "valign": "t", "enforce_size": True } options.update(custom_options) #print "options:", options if table: table.reformat(width=width, height=height, **options) else: table = EvTable(width=width, height=height, **options) mapping[key] = (iyup, leftix, width, height, table) return mapping
def func(self): """Setup the irc-channel mapping""" if not settings.IRC_ENABLED: string = "IRC is not enabled. Activate it in game/settings.py." self.msg(string) return # If no args: list bots. if not self.args: # show all connections ircbots = [ bot for bot in AccountDB.objects.filter( db_is_bot=True, username__startswith="ircbot-") ] if ircbots: from evennia.utils.evtable import EvTable table = EvTable("|w#dbref|n", "|wbotname|n", "|wev-channel/location|n", "|wirc-channel|n", "|wSSL|n", maxwidth=_DEFAULT_WIDTH) for ircbot in ircbots: ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel, ircbot.db.irc_network, ircbot.db.irc_port) table.add_row( "#%i" % ircbot.id, ircbot.db.irc_botname, ircbot.attributes.get("ev_channel", ircbot.db.ev_location.key), ircinfo, ircbot.db.irc_ssl) self.msg(table) self.msg("Use 'help @puppetbot' for more infomation.") else: self.msg("No irc bots found.") return # Switch options available only if valid bot is given. if self.switches: botname = "ircbot-%s" % self.lhs matches = AccountDB.objects.filter(db_is_bot=True, username=botname) dbref = utils.dbref(self.lhs) if not matches and dbref: # try dbref match matches = AccountDB.objects.filter(db_is_bot=True, id=dbref) if not matches: self.msg("No valid bot given. Consult 'help @puppetbot'") return # Puppetbot/delete <bot> - Delete bot. if any(i in ['disconnect', 'remove', 'delete'] for i in self.switches): matches[0].delete() self.msg("IRC link/bot destroyed.") return # Puppetbot/ping <bot> - ping bot. if "ping" in self.switches: matches[0].ping(self.caller) self.msg("Pinging " + self.lhs) return # Puppetbot/about <bot> = msg - Set bot about message. if "about" in self.switches: if self.rhs: matches[0].db.botdesc = self.rhs self.msg("Bot about message changed to: " + self.rhs) else: self.msg("No message given. 'About' desc change aborted.") return # Puppetbot/who <bot> - Get IRC user list.. if "who" in self.switches: # retrieve user list. The bot must handles the echo since it's # an asynchronous call. self.caller.msg( "Requesting nicklist from %s (%s:%s)." % (matches[0].db.irc_channel, matches[0].db.irc_network, matches[0].db.irc_port)) matches[0].get_nicklist(self.caller) return # Puppetbot/reconnect <bot> - reconnect bot. if "reconnect" in self.switches: matches[0].reconnect() self.msg("Reconnecting " + self.lhs) return # Puppetbot/reload <bot> - Delete all bots, recreates bots from new user list. if "reload" in self.switches: matches[0].db.ev_location.msg_contents( "Puppet reload in progress.") puppetlist = [ puppet for puppet in search.search_tag(matches[0].key + "-puppet") ] for puppet in puppetlist: puppet.delete() matches[0].get_nicklist() return # Puppetbot/ignore <bot> = puppet - Toggle ignore IRC user. if "ignore" in self.switches: if self.rhs: user = self.rhs.strip() # If already ignored, toggle off. if user in matches[0].db.userignorelist: matches[0].db.userignorelist.remove(user) matches[0].get_nicklist() return # Else ignore user. else: matches[0].db.userignorelist.append(user) if user in matches[0].db.puppetdict: matches[0].db.puppetdict[user].delete() del matches[0].db.puppetdict[user] return else: self.msg("Usage: Puppetbot/ignore <bot> = <puppet>") return # Puppetbot/entrymsg <bot> = msg - Set default puppet creation message. if "entrymsg" in self.switches: if self.rhs: matches[0].db.puppetentrymsg = " " + self.rhs self.msg("Bot entry message changed to: " + " " + self.rhs) else: self.msg("No message given. Message change aborted.") return # Puppetbot/exitmsg <bot> = msg - Set default puppet deletion message. if "exitmsg" in self.switches: if self.rhs: matches[0].db.puppetexitmsg = " " + self.rhs self.msg("Bot exit message changed to: " + " " + self.rhs) else: self.msg("No message given. Message change aborted.") return # Puppetbot/prefix <bot> = msg - Set string put before username in puppet.key if "prefix" in self.switches: if self.rhs: matches[0].db.puppetprefix = self.rhs self.msg("Puppet prefix changed to: " + self.rhs) self.msg( "Use: '@puppetbot/reload <bot>' to implement changes.") else: self.msg("No message given. Prefix change aborted.") return # Puppetbot/suffix <bot> = msg - Set string put after username in puppet.key if "suffix" in self.switches: if self.rhs: matches[0].db.puppetsuffix = self.rhs self.msg("Puppet suffix changed to: " + self.rhs) self.msg( "Use: '@puppetbot/reload <bot>' to implement changes.") else: self.msg("No message given. Suffix change aborted.") return # Puppetbot/defaultdesc <bot> = msg - Set default puppet desc message. if "defaultdesc" in self.switches: if self.rhs: matches[0].db.puppetlastdesc = matches[ 0].db.puppetdefaultdesc matches[0].db.puppetdefaultdesc = self.rhs self.msg("Default puppet description changed to: " + self.rhs) else: self.msg("No message given. Message change aborted.") return # Puppetbot/softdesc <bot> = msg - Only changes non custom puppet descriptions to new default. if "softdesc" in self.switches: puppetlist = [ puppet for puppet in search.search_tag(matches[0].key + "-puppet") ] for puppet in puppetlist: if puppet.db.desc == matches[0].db.puppetlastdesc: puppet.db.desc = matches[0].db.puppetdefaultdesc self.msg("Puppets description changed to: " + matches[0].db.puppetdefaultdesc) return # Puppetbot/forcedesc <bot> = msg - Changes all puppet descriptions to new default. if "forcedesc" in self.switches: puppetlist = [ puppet for puppet in search.search_tag(matches[0].key + "-puppet") ] for puppet in puppetlist: puppet.db.desc = matches[0].db.puppetdefaultdesc self.msg("Puppets description changed to: " + matches[0].db.puppetdefaultdesc) return # Create Bot. location = self.caller.location self.args = self.args.replace('#', ' ') # Avoid Python comment issues try: irc_network, irc_port, irc_channel, irc_botname = \ [part.strip() for part in self.args.split(None, 4)] irc_channel = "#%s" % irc_channel except Exception: string = "IRC bot definition '%s' is not valid." % self.args self.msg(string) return botname = "ircbot-%s" % irc_botname # create a new bot bot = AccountDB.objects.filter(username__iexact=botname) if bot: # re-use an existing bot bot = bot[0] if not bot.is_bot: self.msg("'%s' already exists and is not a bot." % botname) return else: try: bot = create.create_account(botname, None, None, typeclass=ServerBot) except Exception as err: self.msg("|rError, could not create the bot:|n '%s'." % err) return bot.start(ev_location=location, irc_botname=irc_botname, irc_channel=irc_channel, irc_network=irc_network, irc_port=irc_port) self.msg("Connection created. Starting IRC bot.")
def func(self): caller = self.caller if not self.args: self._usage() return # split off the target from the first item of lhslist targs = tuple(self.lhslist[0].rsplit(' ', 1)) if len(targs) == 1 or \ (len(targs) > 1 and targs[1].upper() not in ALL_TRAITS): target, self.lhslist[0] = self.lhslist[0], '' else: target, self.lhslist[0] = targs # search for the target NPC char = caller.search(target, location=caller.location, typeclass='typeclasses.characters.NPC') if not char: return # the inherits_from check below is necessary due to an issue # with search() on the ContripRPObject class if not inherits_from(char, 'typeclasses.characters.NPC'): caller.msg("Could not find NPC: '{}'".format(target)) return if self.rhs: if not all((x.isdigit() for x in self.rhslist)): caller.msg('Assignment values must be numeric.') return if len(self.lhslist) != len(self.rhslist): caller.msg('Incorrect number of assignment values.') return for i in xrange(len(self.lhslist)): if self.lhslist[i].upper() in char.traits.all: char.traits[self.lhslist[i].upper()].base = \ min(max(int(self.rhslist[i]), 0), 10) caller.msg('Trait "{}" set to {} for {}'.format( self.lhslist[i].upper(), min(max(int(self.rhslist[i]), 0), 10), char.sdesc.get())) else: caller.msg('Invalid trait: "{}"'.format(self.lhslist[i])) # display traits data = [] if any(self.lhslist): traits = [t.upper() for t in self.lhslist if t.upper() in char.traits.all] else: traits = char.traits.all if len(traits) == 0: return elif 0 < len(traits) < 3: [data.append([self._format_trait_3col(char.traits[t])]) for t in traits] else: [data.append([self._format_trait_3col(char.traits[t]) for t in traits[i::3]]) for i in xrange(3)] table = EvTable(header=False, table=data) caller.msg(unicode(table))
def func(self): """List the accounts""" caller = self.caller args = self.args if "delete" in self.switches: account = getattr(caller, "account") if not account or not account.check_permstring("Developer"): caller.msg("You are not allowed to delete accounts.") return if not args: caller.msg("Usage: @accounts/delete <name or #id> [: reason]") return reason = "" if ":" in args: args, reason = [arg.strip() for arg in args.split(":", 1)] # We use account_search since we want to be sure to find also accounts # that lack characters. accounts = search.account_search(args) if not accounts: self.msg("Could not find an account by that name.") return if len(accounts) > 1: string = "There were multiple matches:\n" string += "\n".join(" %s %s" % (account.id, account.key) for account in accounts) self.msg(string) return account = accounts.first() if not account.access(caller, "delete"): self.msg( "You don't have the permissions to delete that account.") return username = account.username # ask for confirmation confirm = ( "It is often better to block access to an account rather than to delete it. " "|yAre you sure you want to permanently delete " "account '|n{}|y'|n yes/[no]?".format(username)) answer = yield (confirm) if answer.lower() not in ('y', 'yes'): caller.msg("Canceled deletion.") return # Boot the account then delete it. self.msg("Informing and disconnecting account ...") string = "\nYour account '%s' is being *permanently* deleted.\n" % username if reason: string += " Reason given:\n '%s'" % reason account.msg(string) logger.log_sec( "Account Deleted: %s (Reason: %s, Caller: %s, IP: %s)." % (account, reason, caller, self.session.address)) account.delete() self.msg("Account %s was successfully deleted." % username) return # No switches, default to displaying a list of accounts. if self.args and self.args.isdigit(): nlim = int(self.args) else: nlim = 10 naccounts = AccountDB.objects.count() # typeclass table dbtotals = AccountDB.objects.object_totals() typetable = EvTable("|wtypeclass|n", "|wcount|n", "|w%%|n", border="cells", align="l") for path, count in dbtotals.items(): typetable.add_row(path, count, "%.2f" % ((float(count) / naccounts) * 100)) # last N table plyrs = AccountDB.objects.all().order_by( "db_date_created")[max(0, naccounts - nlim):] latesttable = EvTable("|wcreated|n", "|wdbref|n", "|wname|n", "|wtypeclass|n", border="cells", align="l") for ply in plyrs: latesttable.add_row(utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.path) string = "\n|wAccount typeclass distribution:|n\n%s" % typetable string += "\n|wLast %s Accounts created:|n\n%s" % (min( naccounts, nlim), latesttable) caller.msg(string)
def evtable_options_formatter(optionlist): if not optionlist: return "" # column separation distance colsep = 4 nlist = len(optionlist) # get the widest option line in the table. table_width_max = -1 table = [] for key, desc in optionlist: if not (key or desc): continue table_width_max = max( table_width_max, max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in desc.split("\n")) + colsep) raw_key = strip_ansi(key) if raw_key != key: # already decorations in key definition table.append( ANSIString(" {lc%s{lt%s{le: %s" % (raw_key, key, desc))) else: # add a default white color to key table.append( ANSIString(" {lc%s{lt{w%s{n{le: %s" % (raw_key, raw_key, desc))) ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1 # number of ncols nlastcol = nlist % ncols # number of elements left in last row # get the amount of rows needed (start with 4 rows) nrows = 4 while nrows * ncols < nlist: nrows += 1 ncols = nlist // nrows # number of full columns nlastcol = nlist % nrows # number of elements in last column # get the final column count ncols = ncols + 1 if nlastcol > 0 else ncols if ncols > 1: # only extend if longer than one column table.extend([" " for i in range(nrows - nlastcol)]) # build the actual table grid table = [ table[icol * nrows:(icol * nrows) + nrows] for icol in range(0, ncols) ] # adjust the width of each column for icol in range(len(table)): col_width = max( max(m_len(p) for p in part.split("\n")) for part in table[icol]) + colsep table[icol] = [ pad(part, width=col_width + colsep, align="l") for part in table[icol] ] # format the table into columns return unicode(EvTable(table=table, border="none"))
def _format_node(self, nodetext, optionlist): """ Format the node text + option section Args: nodetext (str): The node text optionlist (list): List of (key, desc) pairs. Returns: string (str): The options section, including all needed spaces. Notes: This will adjust the columns of the options, first to use a maxiumum of 4 rows (expanding in columns), then gradually growing to make use of the screen space. """ # # handle the node text # nodetext = dedent(nodetext).strip() nodetext_width_max = max(m_len(line) for line in nodetext.split("\n")) if not optionlist: # return the node text "naked". separator1 = "_" * nodetext_width_max + "\n\n" if nodetext_width_max else "" separator2 = "\n" if nodetext_width_max else "" + "_" * nodetext_width_max return separator1 + nodetext + separator2 # # handle the options # # column separation distance colsep = 4 nlist = len(optionlist) # get the widest option line in the table. table_width_max = -1 table = [] for key, desc in optionlist: table_width_max = max( table_width_max, max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in desc.split("\n")) + colsep) raw_key = strip_ansi(key) if raw_key != key: # already decorations in key definition table.append( ANSIString(" {lc%s{lt%s{le: %s" % (raw_key, key, desc))) else: # add a default white color to key table.append( ANSIString(" {lc%s{lt{w%s{n{le: %s" % (raw_key, raw_key, desc))) ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1 # number of ncols nlastcol = nlist % ncols # number of elements left in last row # get the amount of rows needed (start with 4 rows) nrows = 4 while nrows * ncols < nlist: nrows += 1 ncols = nlist // nrows # number of full columns nlastcol = nlist % nrows # number of elements in last column # get the final column count ncols = ncols + 1 if nlastcol > 0 else ncols if ncols > 1: # only extend if longer than one column table.extend([" " for i in xrange(nrows - nlastcol)]) # build the actual table grid table = [ table[icol * nrows:(icol * nrows) + nrows] for icol in xrange(0, ncols) ] # adjust the width of each column total_width = 0 for icol in xrange(len(table)): col_width = max( max(m_len(p) for p in part.split("\n")) for part in table[icol]) + colsep table[icol] = [ pad(part, width=col_width + colsep, align="l") for part in table[icol] ] total_width += col_width # format the table into columns table = EvTable(table=table, border="none") # build the page total_width = max(total_width, nodetext_width_max) separator1 = "_" * total_width + "\n\n" if nodetext_width_max else "" separator2 = "\n" + "_" * total_width + "\n\n" if total_width else "" return separator1 + nodetext + separator2 + unicode(table)
def options_formatter(self, optionlist): """ Formats the option block. Args: optionlist (list): List of (key, description) tuples for every option related to this node. caller (Object, Account or None, optional): The caller of the node. Returns: options (str): The formatted option display. """ if not optionlist: return "" # column separation distance colsep = 4 nlist = len(optionlist) # get the widest option line in the table. table_width_max = -1 table = [] for key, desc in optionlist: if not (key or desc): continue table_width_max = max(table_width_max, max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in desc.split("\n")) + colsep) raw_key = strip_ansi(key) if raw_key != key: # already decorations in key definition table.append(" |lc%s|lt%s|le: %s" % (raw_key, key, desc)) else: # add a default white color to key table.append(" |lc%s|lt|w%s|n|le: %s" % (raw_key, raw_key, desc)) ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1 # number of ncols # get the amount of rows needed (start with 4 rows) nrows = 4 while nrows * ncols < nlist: nrows += 1 ncols = nlist // nrows # number of full columns nlastcol = nlist % nrows # number of elements in last column # get the final column count ncols = ncols + 1 if nlastcol > 0 else ncols if ncols > 1: # only extend if longer than one column table.extend([" " for i in range(nrows - nlastcol)]) # build the actual table grid table = [table[icol * nrows: (icol * nrows) + nrows] for icol in range(0, ncols)] # adjust the width of each column for icol in range(len(table)): col_width = max(max(m_len(p) for p in part.split("\n")) for part in table[icol]) + colsep table[icol] = [pad(part, width=col_width + colsep, align="l") for part in table[icol]] # format the table into columns return unicode(EvTable(table=table, border="none"))
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True): """ Collate a list of found prototypes based on search criteria and access. Args: caller (Account or Object): The object requesting the list. key (str, optional): Exact or partial prototype key to query for. tags (str or list, optional): Tag key or keys to query for. show_non_use (bool, optional): Show also prototypes the caller may not use. show_non_edit (bool, optional): Show also prototypes the caller may not edit. Returns: table (EvTable or None): An EvTable representation of the prototypes. None if no prototypes were found. """ # this allows us to pass lists of empty strings tags = [tag for tag in make_iter(tags) if tag] # get prototypes for readonly and db-based prototypes prototypes = search_prototype(key, tags) # get use-permissions of readonly attributes (edit is always False) display_tuples = [] for prototype in sorted(prototypes, key=lambda d: d.get('prototype_key', '')): lock_use = caller.locks.check_lockstring( caller, prototype.get('prototype_locks', ''), access_type='spawn', default=True) if not show_non_use and not lock_use: continue if prototype.get('prototype_key', '') in _MODULE_PROTOTYPES: lock_edit = False else: lock_edit = caller.locks.check_lockstring( caller, prototype.get('prototype_locks', ''), access_type='edit', default=True) if not show_non_edit and not lock_edit: continue ptags = [] for ptag in prototype.get('prototype_tags', []): if is_iter(ptag): if len(ptag) > 1: ptags.append("{} (category: {}".format(ptag[0], ptag[1])) else: ptags.append(ptag[0]) else: ptags.append(str(ptag)) display_tuples.append( (prototype.get('prototype_key', '<unset>'), prototype.get('prototype_desc', '<unset>'), "{}/{}".format('Y' if lock_use else 'N', 'Y' if lock_edit else 'N'), ",".join(ptags))) if not display_tuples: return "" table = [] width = 78 for i in range(len(display_tuples[0])): table.append([str(display_tuple[i]) for display_tuple in display_tuples]) table = EvTable("Key", "Desc", "Spawn/Edit", "Tags", table=table, crop=True, width=width) table.reformat_column(0, width=22) table.reformat_column(1, width=29) table.reformat_column(2, width=11, align='c') table.reformat_column(3, width=16) return table
def menunode_allocate_skills(caller, raw_string): """Skill -1 counter allocation menu node.""" char = caller.new_char sk = char.skills total = 3 counts = {1: 'one', 2: 'two', 3: 'three'} plusses = (ceil(char.traits.INT.actual / 3.0) - sum(sk[s].plus for s in skills.ALL_SKILLS)) minuses = total - sum(sk[s].minus for s in skills.ALL_SKILLS) text = "" raw_string = raw_string.strip() if raw_string.isdigit() and int(raw_string) <= len(skills.ALL_SKILLS): skill = sk[skills.ALL_SKILLS[int(raw_string) - 1]] if minuses: if skill.actual - skill.minus - 1 > 0: skill.minus += 1 minuses -= 1 else: text += "|rSkills cannot be reduced below one.|n\n" elif plusses: if skill.actual + skill.plus + 1 <= 10: skill.plus += 1 plusses -= 1 else: text += "|rSkills cannot be increased above ten.|n\n" if plusses or minuses: text += "{}\n\n".format(raw_string) if raw_string and raw_string[0] == 'F' else "" text += "Your ability to perform actions in Ainneve is\n" text += "tied to your character's skills.\n" if minuses: text += "Please allocate |w{}|n |m'-1'|n counter{}.".format( counts[minuses], 's' if minuses != 1 else '') elif plusses: text += "Please allocate |w{}|n |m'+1'|n counter{}.".format( counts[plusses], 's' if plusses != 1 else '') help = "Skill allocation is a two step process. First, you\n" help += "distribute three '-1' counters across your skills,\n" help += "then a number of '+1' counters equal to your Intelligence\n" help += "divided by 3." options = [{"desc": _format_skill_opts(sk[s]), "goto": "menunode_allocate_skills"} for s in skills.ALL_SKILLS] def clear_skills(s): """Reset plus and minus counters on all skills.""" for skill in skills.ALL_SKILLS: sk[skill].plus = 0 sk[skill].minus = 0 options.append({"desc": "Start Over", "exec": clear_skills, "goto": "menunode_allocate_skills"}) return (text, help), options else: skills.finalize_skills(char.skills) data = [] for i in list(range(3)): data.append([_format_trait_opts(sk[s], color='|M') for s in skills.ALL_SKILLS[i::3]]) table = EvTable(header=False, table=data) output = "Final Skills:\n" output += "{skills}\n" char.db.wallet['SC'] = d_roll('2d6+3') output += "You begin with |w{sc} SC|n (Silver Coins)." return menunode_equipment_cats( caller, output.format(skills=table, sc=char.db.wallet['SC']) )
def func(self): """Setup the irc-channel mapping""" if not settings.IRC_ENABLED: string = "IRC is not enabled. Activate it in game/settings.py." self.msg(string) return # If no args: list bots. if not self.args: # show all connections ircbots = [ bot for bot in AccountDB.objects.filter( db_is_bot=True, username__startswith="ircbot-") ] if ircbots: from evennia.utils.evtable import EvTable table = EvTable("|w#dbref|n", "|wbotname|n", "|wev-channel/location|n", "|wirc-channel|n", "|wSSL|n", maxwidth=_DEFAULT_WIDTH) for ircbot in ircbots: ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel, ircbot.db.irc_network, ircbot.db.irc_port) table.add_row( "#%i" % ircbot.id, ircbot.db.irc_botname, ircbot.attributes.get("ev_channel", ircbot.db.ev_location.key), ircinfo, ircbot.db.irc_ssl) self.msg(table) self.msg("Use 'help @puppetbot' for more infomation.") else: self.msg("No irc bots found.") return # Switch options available only if valid bot is given. if self.switches: botname = "ircbot-%s" % self.lhs matches = AccountDB.objects.filter(db_is_bot=True, username=botname) dbref = utils.dbref(self.lhs) if not matches and dbref: # try dbref match matches = AccountDB.objects.filter(db_is_bot=True, id=dbref) if not matches: self.msg("No valid bot given. Consult 'help @puppetbot'") return # Puppetbot/delete <bot> - Delete bot. if any(i in ['disconnect', 'remove', 'delete'] for i in self.switches): matches[0].delete() self.msg("IRC link/bot destroyed.") return # Puppetbot/ping <bot> - ping bot. if "ping" in self.switches: matches[0].ping(self.caller) self.msg("Pinging " + self.lhs) return # Puppetbot/who <bot> - Get IRC user list.. if "who" in self.switches: # retrieve user list. The bot must handles the echo since it's # an asynchronous call. self.caller.msg( "Requesting nicklist from %s (%s:%s)." % (matches[0].db.irc_channel, matches[0].db.irc_network, matches[0].db.irc_port)) matches[0].get_nicklist(self.caller) return # Puppetbot/reconnect <bot> - reconnect bot. if "reconnect" in self.switches: matches[0].reconnect() self.msg("Reconnecting " + self.lhs) return # Puppetbot/reload <bot> - Delete all bots, recreates bots from new user list. if "reload" in self.switches: matches[0].db.ev_location.msg_contents( "Puppet reload in progress.") puppetlist = [ puppet for puppet in search.search_tag(matches[0].key + "-puppet") ] for puppet in puppetlist: puppet.delete() matches[0].get_nicklist() return # Create Bot. location = self.caller.location self.args = self.args.replace('#', ' ') # Avoid Python comment issues try: irc_network, irc_port, irc_channel, irc_botname = \ [part.strip() for part in self.args.split(None, 4)] irc_channel = "#%s" % irc_channel except Exception: string = "IRC bot definition '%s' is not valid." % self.args self.msg(string) return botname = "ircbot-%s" % irc_botname # create a new bot bot = AccountDB.objects.filter(username__iexact=botname) if bot: self.msg("Account '%s' already exists." % botname) return else: password = "******" try: bot = create.create_account(botname, None, password, typeclass=AccountBot) except Exception as err: self.msg("|rError, could not create the bot:|n '%s'." % err) return bot.start(ev_location=location, irc_botname=irc_botname, irc_channel=irc_channel, irc_network=irc_network, irc_port=irc_port) self.msg("Connection created. Starting IRC bot.")
def func(self): """Implement command""" caller = self.caller switches = self.switches if switches and switches[0] not in ("list", "start", "stop", "delete"): caller.msg("Usage: @service/<list|start|stop|delete> [servicename]") return # get all services service_collection = SESSIONS.server.services if not switches or switches[0] == "list": # Just display the list of installed services and their # status, then exit. table = EvTable("|wService|n (use @services/start|stop|delete)", "|wstatus", align="l") for service in service_collection.services: table.add_row(service.name, service.running and "|gRunning" or "|rNot Running") caller.msg(unicode(table)) return # Get the service to start / stop try: service = service_collection.getServiceNamed(self.args) except Exception: string = 'Invalid service name. This command is case-sensitive. ' string += 'See @service/list for valid service name (enter the full name exactly).' caller.msg(string) return if switches[0] in ("stop", "delete"): # Stopping/killing a service gracefully closes it and disconnects # any connections (if applicable). delmode = switches[0] == "delete" if not service.running: caller.msg('That service is not currently running.') return if service.name[:7] == 'Evennia': if delmode: caller.msg("You cannot remove a core Evennia service (named 'Evennia***').") return string = "You seem to be shutting down a core Evennia service (named 'Evennia***'). Note that" string += "stopping some TCP port services will *not* disconnect users *already*" string += "connected on those ports, but *may* instead cause spurious errors for them. To " string += "safely and permanently remove ports, change settings file and restart the server." caller.msg(string) if delmode: service.stopService() service_collection.removeService(service) caller.msg("Stopped and removed service '%s'." % self.args) else: service.stopService() caller.msg("Stopped service '%s'." % self.args) return if switches[0] == "start": # Attempt to start a service. if service.running: caller.msg('That service is already running.') return caller.msg("Starting service '%s'." % self.args) service.startService()
def func(self): "Setup the irc-channel mapping" if not settings.IRC_ENABLED: string = """IRC is not enabled. You need to activate it in game/settings.py.""" self.msg(string) return if 'list' in self.switches: # show all connections ircbots = [bot for bot in PlayerDB.objects.filter(db_is_bot=True, username__startswith="ircbot-")] if ircbots: from evennia.utils.evtable import EvTable table = EvTable("{wdbid{n", "{wbotname{n", "{wev-channel{n", "{wirc-channel{n", maxwidth=_DEFAULT_WIDTH) for ircbot in ircbots: ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel, ircbot.db.irc_network, ircbot.db.irc_port) table.add_row(ircbot.id, ircbot.db.irc_botname, ircbot.db.ev_channel, ircinfo) self.caller.msg(table) else: self.msg("No irc bots found.") return if('disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches): botname = "ircbot-%s" % self.lhs matches = PlayerDB.objects.filter(db_is_bot=True, username=botname) dbref = utils.dbref(self.lhs) if not matches and dbref: # try dbref match matches = PlayerDB.objects.filter(db_is_bot=True, id=dbref) if matches: matches[0].delete() self.msg("IRC connection destroyed.") else: self.msg("IRC connection/bot could not be removed, does it exist?") return if not self.args or not self.rhs: string = "Usage: @irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>" self.msg(string) return channel = self.lhs self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues try: irc_network, irc_port, irc_channel, irc_botname = \ [part.strip() for part in self.rhs.split(None, 3)] irc_channel = "#%s" % irc_channel except Exception: string = "IRC bot definition '%s' is not valid." % self.rhs self.msg(string) return botname = "ircbot-%s" % irc_botname # create a new bot bot = PlayerDB.objects.filter(username__iexact=botname) if bot: # re-use an existing bot bot = bot[0] if not bot.is_bot: self.msg("Player '%s' already exists and is not a bot." % botname) return else: bot = create.create_player(botname, None, None, typeclass=bots.IRCBot) bot.start(ev_channel=channel, irc_botname=irc_botname, irc_channel=irc_channel, irc_network=irc_network, irc_port=irc_port) self.msg("Connection created. Starting IRC bot.")
def func(self): """Setup the rss-channel mapping""" # checking we have all we need if not settings.RSS_ENABLED: string = """RSS is not enabled. You need to activate it in game/settings.py.""" self.msg(string) return try: import feedparser assert feedparser # to avoid checker error of not being used except ImportError: string = "RSS requires python-feedparser (https://pypi.python.org/pypi/feedparser)." \ " Install before continuing." self.msg(string) return if 'list' in self.switches: # show all connections rssbots = [bot for bot in AccountDB.objects.filter(db_is_bot=True, username__startswith="rssbot-")] if rssbots: from evennia.utils.evtable import EvTable table = EvTable("|wdbid|n", "|wupdate rate|n", "|wev-channel", "|wRSS feed URL|n", border="cells", maxwidth=_DEFAULT_WIDTH) for rssbot in rssbots: table.add_row(rssbot.id, rssbot.db.rss_rate, rssbot.db.ev_channel, rssbot.db.rss_url) self.msg(table) else: self.msg("No rss bots found.") return if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches: botname = "rssbot-%s" % self.lhs matches = AccountDB.objects.filter(db_is_bot=True, db_key=botname) if not matches: # try dbref match matches = AccountDB.objects.filter(db_is_bot=True, id=self.args.lstrip("#")) if matches: matches[0].delete() self.msg("RSS connection destroyed.") else: self.msg("RSS connection/bot could not be removed, does it exist?") return if not self.args or not self.rhs: string = "Usage: @rss2chan[/switches] <evennia_channel> = <rss url>" self.msg(string) return channel = self.lhs url = self.rhs botname = "rssbot-%s" % url # create a new bot bot = AccountDB.objects.filter(username__iexact=botname) if bot: # re-use existing bot bot = bot[0] if not bot.is_bot: self.msg("Account '%s' already exists and is not a bot." % botname) return else: bot = create.create_account(botname, None, None, typeclass=bots.RSSBot) bot.start(ev_channel=channel, rss_url=url, rss_rate=10) self.msg("RSS reporter created. Fetching RSS.")
def options_formatter(self, optionlist, options_format): """ Formats the option block. Args: optionlist (list): List of (key, description) tuples for every option related to this node. caller (Object, Account or None, optional): The caller of the node. Returns: options (str): The formatted option display. """ if not optionlist: return "" # column separation distance colsep = 4 # parse out options for Quit, Back, Proceed, None, and Finish option_list_1 = [] option_list_2 = [] for item in optionlist: if ('move_keys' in options_format and item[0] in options_format['move_keys']): option_list_2.append(item) elif ('hide_keys' in options_format and item[0] in options_format['hide_keys']): pass else: option_list_1.append(item) nlist = len(option_list_1) # get the widest option line in the table. table_width_max = -1 table = [] for key, desc in option_list_1: if key or desc: desc_string = ": %s" % desc if desc else "" table_width_max = max( table_width_max, max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in desc_string.split("\n")) + colsep, ) raw_key = strip_ansi(key) if raw_key != key: # already decorations in key definition table.append(" |lc%s|lt%s|le%s" % (raw_key, key, desc_string)) else: # add a default white color to key table.append(" |lc%s|lt|w%s|n|le%s" % (raw_key, raw_key, desc_string)) ncols = _MAX_TEXT_WIDTH // table_width_max # number of ncols if ncols < 0: # no visible option at all return "" ncols = ncols + 1 if ncols == 0 else ncols # get the amount of rows needed (start with 4 rows) if 'rows' in options_format: nrows = options_format['rows'] else: nrows = 4 while nrows * ncols < nlist: nrows += 1 ncols = nlist // nrows # number of full columns nlastcol = nlist % nrows # number of elements in last column # get the final column count ncols = ncols + 1 if nlastcol > 0 else ncols if ncols > 1: # only extend if longer than one column table.extend([" " for i in range(nrows - nlastcol)]) # build the actual table grid table = [ table[icol * nrows:(icol * nrows) + nrows] for icol in range(0, ncols) ] # adjust the width of each column for icol in range(len(table)): col_width = (max( max(m_len(p) for p in part.split("\n")) for part in table[icol]) + colsep) table[icol] = [ pad(part, width=col_width + colsep, align="l") for part in table[icol] ] result = EvTable(table=table, border='none') if len(option_list_2) > 0: result.add_row(' ') for key, desc in option_list_2: if key or desc: desc_string = ": %s" % desc if desc else "" table_width_max = max( table_width_max, max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in desc_string.split("\n")) + colsep, ) raw_key = strip_ansi(key) if raw_key != key: # already decorations in key definition result.add_row(" |lc%s|lt%s|le%s" % (raw_key, key, desc_string)) else: # add a default white color to key result.add_row(" |lc%s|lt|w%s|n|le%s" % (raw_key, raw_key, desc_string)) # format the table into columns return str(result)
def accept_callback(self): """Accept a callback.""" obj = self.obj callback_name = self.callback_name parameters = self.parameters # If no object, display the list of callbacks to be checked if obj is None: table = EvTable("ID", "Type", "Object", "Name", "Updated by", "On", width=78) table.reformat_column(0, align="r") now = datetime.now() for obj, name, number in self.handler.db.to_valid: callbacks = self.handler.get_callbacks(obj).get(name) if callbacks is None: continue try: callback = callbacks[number] except IndexError: continue type_name = obj.typeclass_path.split(".")[-1] by = callback.get("updated_by") by = by.key if by else "|gUnknown|n" updated_on = callback.get("updated_on") if updated_on is None: updated_on = callback.get("created_on") if updated_on: updated_on = "{} ago".format( time_format((now - updated_on).total_seconds(), 4).capitalize()) else: updated_on = "|gUnknown|n" table.add_row(obj.id, type_name, obj, name, by, updated_on) self.msg(unicode(table)) return # An object was specified callbacks = self.handler.get_callbacks(obj) types = self.handler.get_events(obj) # If no callback name is specified, display the list of callbacks if not callback_name: self.list_callbacks() return # Check that the callback exists if callback_name not in callbacks: self.msg("The callback name {} can't be found in {}.".format( callback_name, obj)) return if not parameters: self.msg( "Which callback do you wish to accept? Specify a number.") self.list_callbacks() return # Check that the parameter points to an existing callback try: number = int(parameters) - 1 assert number >= 0 callback = callbacks[callback_name][number] except (ValueError, AssertionError, IndexError): self.msg("The callback {} {} cannot be found in {}.".format( callback_name, parameters, obj)) return # Accept the callback if callback["valid"]: self.msg("This callback has already been accepted.") else: self.handler.accept_callback(obj, callback_name, number) self.msg("The callback {} {} of {} has been accepted.".format( callback_name, parameters, obj))
def func(self): if 'all' in self.switches: spawners = search_script_tag(SPAWNER_TAG, TAG_CATEGORY_BUILDING) if len(spawners) == 0: self.caller.msg("No spawners exist.") return table = EvTable("Scr #", "Spawn Name", "Loc #", "Location", "Respawn", border="cells") for spawner in spawners: if spawner.obj: location_dbref = spawner.obj.dbref location_key = spawner.obj.key else: location_dbref = "N/A" location_key = "No Location" respawn_indicator = spawner.respawn_timer() table.add_row(spawner.dbref, spawner.db.spawn_name, location_dbref, location_key, respawn_indicator) output = "|wSpawners by location:|n\n{}".format(table) self.caller.msg(output) return caller = self.caller if not caller.location: caller.msg("You need to be in a location to check for spawners.") return if not self.switches: if not self.rhs: # List out details for all spawners in the current room spawners = [] for script in caller.location.scripts.all(): if isinstance(script, Spawner): spawners.append(script) if len(spawners) == 0: self.caller.msg( "No spawners are present in this location.") return separator = "\n{}".format("-" * 60) output = "|wSpawners in this location:|n" for spawner in spawners: output += separator output += "\nScript Name: {} ({})".format( spawner.key, spawner.dbref) output += "\nSpawn Target: {} ({})".format( spawner.db.spawn_name, spawner.db.spawn_type) output += "\nAliases: {}".format(','.join( spawner.db.aliases)) output += "\nRespawn Timer: {}".format( spawner.respawn_timer()) output += "\nSpawned Attributes:" if not spawner.db.attributes: output += " None" else: for key, value in spawner.db.attributes.items(): output += "\n {}: {}".format(key, value) output += "\nLocks: {}".format(spawner.db.lockstring) self.caller.msg(output) return # Create a new spawner try: class_from_module(self.rhs) except ImportError: caller.msg( "{} is not a valid class path.\n" "Usage: @spawner <obj name>[;alias1;alias2] = <class path>" .format(self.rhs)) return aliases = self.lhs.split(';') spawn_name = aliases.pop(0) # Force all aliases to be in lowercase aliases = [alias.lower() for alias in aliases] spawner = create_script( typeclass=Spawner, key=Spawner.get_key(spawn_name), obj=caller.location, interval=RESPAWN_TICK_RATE, start_delay=True, persistent=True, autostart=False, desc="Respawns target on a cadence if target is missing.") spawner.tags.add(SPAWNER_TAG, TAG_CATEGORY_BUILDING) spawner.db.spawn_name = spawn_name spawner.db.aliases = aliases spawner.db.spawn_type = self.rhs spawner.start() caller.msg("Successfully started spawner {}({})".format( spawner.db.spawn_name, spawner.dbref)) return # Parse switches off the spawner search params and find it split_left = self.lhs.split('/', 1) spawner = self.find_spawner(split_left.pop(0)) if not spawner: return if "spawn" in self.switches: spawned = spawner.spawn_target() caller.msg("Force spawned {} from spawner {}".format( spawned.get_display_name(caller), spawner.dbref)) return if "del" in self.switches: spawner_name = spawner.name spawner_dbref = spawner.dbref spawner.stop() caller.msg("Stopped spawner {}({})".format(spawner_name, spawner_dbref)) return elif "name" in self.switches: if not self.rhs: caller.msg( "@spawner/name <script #> = <obj name>[;alias1;alias2;...]" ) return aliases = self.rhs.split(';') spawn_name = aliases.pop(0) # Force all aliases to be in lowercase aliases = [alias.lower().strip() for alias in aliases] spawner.db.aliases.extend(aliases) # Remove duplicates spawner.db.aliases = list(set(spawner.db.aliases)) spawner.db.spawn_name = spawn_name spawner.key = Spawner.get_key(spawn_name) caller.msg("Renamed spawn target for spawner {} to be {}".format( spawner.dbref, spawn_name)) return elif "alias" in self.switches: if not self.rhs: spawner.db.aliases = [] caller.msg("Cleared aliases on spawner {}({})".format( spawner.db.spawn_name, spawner.dbref)) return aliases = self.rhs.split(',') # Force all aliases to be in lowercase aliases = [alias.lower().strip() for alias in aliases] spawner.db.aliases.extend(aliases) # Remove duplicates spawner.db.aliases = list(set(spawner.db.aliases)) caller.msg("New alias list for spawner {}({}): {}".format( spawner.db.spawn_name, spawner.dbref, ','.join(spawner.db.aliases))) return elif "type" in self.switches: if not self.rhs: caller.msg("@spawner/type <script #> = <class path>") return try: spawn_class = class_from_module(self.rhs) except ImportError: caller.msg( "{} is not a valid class path, not changing spawn class.". format(self.rhs)) return spawner.db.spawn_type = self.rhs spawner.ndb.spawn_class = spawn_class caller.msg("Changed spawner class for {}({}) to be {}".format( spawner.db.spawn_name, spawner.dbref, self.rhs)) return elif "rate" in self.switches: spawner.respawn_rate = int(self.rhs) caller.msg( "Changed respawn rate for {}({}) to be {} seconds.".format( spawner.db.spawn_name, spawner.dbref, spawner.respawn_rate)) return elif "set" in self.switches: # Check if we chose an attribute; there should be one more element left in split_left if len(split_left) != 1: caller.msg("Usage: @spawner/set <script #>/<attr>[ = <value>]") return attr = split_left[0] # Trying to clear an attribute if present if not self.rhs: if attr in spawner.db.attributes: del spawner.db.attributes[attr] caller.msg( "Removed attribute {} from appearing on spawned objects on {}({})" .format(attr, spawner.db.spawn_name, spawner.dbref)) return else: caller.msg("No attribute {} set for spawner {}({})".format( attr, spawner.db.spawn_name, spawner.dbref)) return # We are setting or overwriting an attribute to be placed on spawned objects value = _convert_from_string(self, self.rhs) spawner.db.attributes[attr] = value caller.msg( "Set attribute {} = {} to appear on objects spawned from {}({})" .format(attr, self.rhs, spawner.db.spawn_name, spawner.dbref)) return elif "setclear" in self.switches: spawner.db.attributes = {} caller.msg( "Cleared all attributes from appearing on objects spawned from {}({})" .format(spawner.db.spawn_name, spawner.dbref)) return elif "lock" in self.switches: spawner.add_lock(self.rhs) caller.msg("Added lock {} to spawner {}({})".format( self.rhs, spawner.db.spawn_name, spawner.dbref)) return elif "lockdel" in self.switches: if self.rhs or len(split_left) != 1: caller.msg("Usage: @spawner/lockdel <script #>/<lock type>") lock_type = split_left[0] spawner.remove_lock(lock_type) caller.msg("Deleted lock {} to spawner {}({})".format( lock_type, spawner.db.spawn_name, spawner.dbref)) return elif "lockreset" in self.switches: spawner.reset_locks() caller.msg("Reset locks to default on spawner {}({})".format( spawner.db.spawn_name, spawner.dbref)) return else: caller.msg( "Invalid switch. Type \"help @spawner\" for a list of valid switches." ) return