Grim Reaper (#666)

(an instance of Generic Automaton made by Dredful)

     A dark presence. Regarding you with hollow eyes, the figure in black shifts with a dry rustle of clacking bones. A menacing step towards you, gleaming scythe held at ready to strike those whose time has come.

Go to location of this object, Attic.



VERB SOURCE CODE:

@goners:
"Syntax: @goners from Grim Reaper";
"";
"Displays all currently reapable players who haven't logged in for greater than one 
month, broken down by months unconnected.";
pn = abs(player.linelen);
player:tell("Showing reapable players, generated ", player:ctime(this.last_run), 
":");
player:tell($string_utils:space(pn, "-"));
player:tell_lines({"Information is in DD:HH:MM:SS format.", "D = has a description, 
O = owns objects, N = never connected"});
for ind in [1..length(this.reapable)]
    suspend(0);
    if (set = this.reapable[ind])
        if (ind != 6)
            player:tell($string_utils:center(tostr(" Not logged in > ", ind, " month", 
(ind == 1) ? "" | "s", " and < ", ind + 1, " months "), pn, "="));
        else
            player:tell($string_utils:center(tostr(" Not logged in > 6 months "), 
pn, "="));
        endif
        newset = {};
        for dude in (this:sorted(set))
            nl = (pn - 38) / 2;
            cleard = (!is_clear_property(dude[1], "description")) ? "D" | " ";
            owned = setremove(dude[1].owned_objects, dude[1]) ? "O" | " ";
            never = is_clear_property(dude[1], "first_connect_time") ? "N" | " ";
            newset = {@newset, tostr($string_utils:left($string_utils:nn(dude[1]), 
nl)[1..nl], cleard, owned, never, $string_utils:right(tostr(" => ", $time_utils:dhms(dude[2])), 
15))};
        endfor
        player:tell_lines({@$string_utils:columnize(newset, 2), ""});
    else
        player:tell("None not logged in greater than ", ind, " month", (ind == 1) 
? "" | "s", (ind == 6) ? "." | tostr(" and less than ", ind + 1, " months."));
    endif
endfor
player:tell($string_utils:space(pn, "-"));
.


sweep:
if (!$perm_utils:controls(caller_perms(), this))
    return E_PERM;
endif
if ($code_utils:task_valid(this.sweep_task))
    kill_task(this.sweep_task);
endif
fork sweeper (0)
    while (this.running)
        this.reapable = {{}, {}, {}, {}, {}, {}};
        for victim in (players())
            if (!(victim in this.exempt))
                time = time() - victim.last_disconnect_time;
                if (ind = $math_utils:div(time, (24 * 3600) * 30))
                    ind = (ind > 6) ? 6 | ind;
                    this.reapable[ind] = setadd(this.reapable[ind], victim);
                endif
            endif
            $command_utils:suspend_if_needed(0);
        endfor
        this.last_run = time();
        suspend(this.sweep_delay);
    endwhile
endfork
this.sweep_task = sweeper;
.


sorted:
set = args[1];
newset = {};
for d in (set)
    newset = {@newset, {d, time() - d.last_disconnect_time}};
endfor
return $list_utils:sort_alist(newset, 2);
.


@check-email:
"Checks through players() for email addresses that match what $registration_db thinks 
they have.";
if (!$perm_utils:controls(player, this))
    player:tell(E_PERM);
    return;
endif
player:tell("Generating report...");
blank_email = not_match = not_in_db = dual_db = {};
for who in (players())
    if ((!$object_utils:isa(who, $guest)) && (!(who in {$no_one, $hacker, $housekeeper, 
$quota})))
        if (info = $registration_db:get_player(who))
            if (length(info) > 1)
                dual_db = {@dual_db, who};
            elseif (info[1][1] != who.email_address)
                not_match = {@not_match, who};
            endif
        else
            not_in_db = {@not_in_db, who};
        endif
        if (!who.email_address)
            blank_email = {@blank_email, who};
        endif
        $command_utils:suspend_if_needed(0);
    endif
endfor
if (dual_db)
    player:tell("Players found with more than one email in $registration_db: ", $string_utils:nanl(dual_db));
    player:tell("----------");
endif
if (not_match)
    player:tell("Players found with email addresses not matching $registration_db: 
", $string_utils:nanl(not_match));
    player:tell("----------");
endif
if (not_in_db)
    player:tell("Players found who aren't in $registration_db: ", $string_utils:nanl(not_in_db));
    player:tell("----------");
endif
if (blank_email)
    player:tell("Players found with blank .email_address: ", $string_utils:nanl(blank_email));
    player:tell("----------");
endif
full = $set_utils:union(dual_db, not_match, not_in_db, blank_email);
fixed = $set_utils:diff(this.no_email, full);
new = $set_utils:diff(full, this.no_email);
if (new)
    player:tell("New problems since the last run of this verb: ", $string_utils:nanl(new));
    player:tell("----------");
endif
if (fixed)
    player:tell("Fixed since the last run of this verb: ", $string_utils:nanl(fixed));
    player:tell("----------");
endif
this.no_email = full;
player:tell("-- Finished");
.


@reap:
"Gets rid of players completely and cleanly. Does lots of checks first. Lots of places 
to opt out of the process before any damage is done.";
if (!player.wizard)
    player:tell(E_PERM);
    return;
endif
victim = $string_utils:match_player(dobjstr);
if ($command_utils:player_match_failed(victim, dobjstr))
    return;
endif
nvictim = $string_utils:nn(victim);
ind = 0;
for set in [1..length(this.reapable)]
    ind = (victim in this.reapable[set]) ? set | ind;
endfor
if (!ind)
    msg = tostr(nvictim, " has not been marked as reapable by the Grim Reaper as 
of ", player:ctime(this.last_run), ". Continue anyway?");
else
    if (ind != 6)
        msg = tostr(nvictim, " has not logged in > ", ind, " month", (ind == 1) ? 
"" | "s", " and < ", ind + 1, " months.");
    else
        msg = tostr(nvictim, " has not logged in > 6 months.");
    endif
    msg = msg + " Continue?";
endif
if (!$command_utils:yes_or_no(msg))
    return;
endif
if (length(email = $registration_db:get_player(victim)) > 1)
    player:tell("");
    player:tell(nvictim, " has multiple email addresses. Will remove all occurences 
of ", victim.name, " in $registration_db.");
endif
if (all_seconds = $local.second_char_registry:all_second_chars(victim))
    if (!$command_utils:yes_or_no(tostr(nvictim, " is a registered second character 
of: ", $string_utils:nanl(setremove(all_seconds, victim)), ". If you continue, the 
association will be removed, but it might be wise to abort and check to see how idle 
the other characters are first. Continue?")))
        return;
    endif
endif
suspend(0);
if (owned = setremove(victim.owned_objects, victim))
    player:tell("");
    if (!$command_utils:yes_or_no(tostr(nvictim, " owns objects. If you continue, 
they will be @chowned to $hacker. The .orphan property of the Grim Reaper will keep 
a list of these objects. It is probably better to abort now and take care of the 
objects by hand first, then reap. Continue?")))
        return;
    endif
    for thing in (owned)
        $wiz_utils:set_owner(thing, $hacker);
    endfor
    this.orphans = {@this.orphans, {nvictim, owned}};
endif
suspend(0);
if (email)
    for set in (email)
        addr = set[1];
        who = set[2];
        nind = $list_utils:iassoc(victim, who);
        if (length(who[nind]) > 1)
            who[nind][2] = tostr("Reaped ", $time_utils:ddmmyy(time()));
        else
            who[nind] = {@who[nind], tostr("Reaped ", $time_utils:ddmmyy(time()))};
        endif
        $registration_db:insert(addr, who);
    endfor
endif
if (all_seconds)
    $local.second_char_registry:unsecond_char(victim, setremove(all_seconds, victim)[1]);
endif
if (ind)
    this.reapable[ind] = setremove(this.reapable[ind], victim);
endif
suspend(0);
$recycler:_recycle(victim);
player:tell("With a flash of steel, and an almost soundless slice through the air, 
", this.name, " takes its latest victim, ", nvictim, ".");
this.location:announce(this.name, " quickly whirls its scythe through insubstantiality 
and takes another victim.");
.



PROPERTY DATA:
      reapable
      exempt
      sweep_task
      sweep_delay
      last_run
      no_email
      orphans
      running
      problem_email