/
CDC-1.2b/
CDC-1.2b/src/
parent $utilities
object $channels

var $root child_index 0
var $root owners [$channels]
var $root fertile 0
var $root inited 1
var $channels channels ['all, 'chatter, 'System, 'Admins]
var $root blessed [$interaction]
var $channels tuners #[['all, 0], ['chatter, 1], ['System, 1], ['Admins, 1]]
var $channels senders #[['all, 'admins], ['chatter, 1], ['System, 'admins], ['Admins, 1]]
var $channels owners #[]
var $channels channel_owner #[['all, $channels], ['chatter, $channels], ['System, $channels], ['Admins, $channels]]
var $channels listeners #[['all, 'connected], ['chatter, []], ['System, []], ['Admins, []]]
var $channels max_channels_owned 3
var $root owned [$channels]
var $root manager $channels
var $root writable [$channels]
var $root readable ['parameters, 'methods, 'code]
var $root trusted [$housekeeper]
var $root dbref 'channels

method __admins
    return ($sys.system()) + [$login_interface];
.

method del_channel
    arg channel;
    var c;
    
    if ((sender() != (channel_owner[channel])) && (!(.is_writable_by(sender()))))
        throw(~perm, "Sender is not the owner of the channel");
    ._del_channel(channel);
    c = owners[sender()];
    if (c == 1)
        owners = dict_del(owners, sender());
    else
        owners = dict_add(owners, sender(), c - 1);
.

method validate_channel
    arg channel, [rest];
    var x, chars;
    
    // receives a string, returns a symbol if valid
    chars = $string.strip($string.non_alphanumeric(), "-_");
    for x in [1 .. strlen(chars)] {
        if ((chars[x]) in channel)
            throw(~type, "Channel name can only contain alphanumeric characters");
    }
    if (!channel)
        throw(~type, "Channel cannot be blank");
    if (!rest) {
        channel = .match_channel(channel);
        if (!channel)
            throw(~channenf, "Channel does not exist");
        if (listlen(channel) > 1)
            throw(~ambig, "Channel matches all of: " + ($data.unparse(channel)));
        channel = channel[1];
    } else {
        channel = tosym(channel);
    }
    return channel;
.

method all_owners
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender not an owner");
    return owners;
.

method all_listeners
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender not an owner");
    return listeners;
.

method all_senders
    if (!(.is_writable_by(sender())))
        throw(~perm, "Sender not an owner");
    return senders;
.

method listeners
    arg channel;
    var ret;
    
    // if (!.is_writable_by(sender()))
    //   throw(~perm, "Sender not an owner");
    if (type(listeners[channel]) == 'symbol)
        ret = .(tosym("__" + tostr(listeners[channel])))();
    else
        ret = listeners[channel];
    if (!((sender() == this()) || (sender() in ret)))
        return [];
    else
        return ret;
.

method senders
    arg channel;
    
    if (sender() != this())
        throw(~perm, "Sender not an owner");
    if (type(senders[channel]) == 'symbol)
        return .(tosym("__" + tostr(senders[channel])))();
    return senders[channel];
.

method match_channel
    arg channel_name;
    var channel_matches, channel;
    
    channel_matches = [];
    channel = tosym(channel_name) in channels;
    if (channel)
        return [channels[channel]];
    for channel in (channels) {
        if (match_begin(tostr(channel), channel_name))
            channel_matches = [@channel_matches, channel];
    }
    return channel_matches;
.

method allow_send_to
    arg channel, who, [caller];
    var senders;
    
    senders = .senders(channel);
    if (senders == 0)
        throw(~locked, "Channel is locked");
    if (senders == 1)
        return 1;
    if (type(senders) == 'list) {
        if (who in senders)
            return 1;
    }
    if (who == (channel_owner[channel]))
        return 1;
    if ((channel == 'System) && caller)
        return (caller[1]) in senders;
    return 0;
.

method allow_tune_in
    arg channel, who;
    
    if ((.senders(channel)) == 0)
        throw(~locked, "Channel is locked");
    if ((tuners[channel]) == 1)
        return 1;
    if (type(tuners[channel]) == 'list) {
        if (who in (tuners[channel]))
            return 1;
    }
    if (who == (channel_owner[channel]))
        return 1;
    return 0;
.

method channels
    return channels;
.

method set_channel
    arg channel_str, [args];
    var channel, allowed, senders, listeners;
    
    // args: channel_str, allowed, senders, listeners
    // channel setup:  #[['namef, [[allowed], [senders], owner, [listeners]]]];
    channel = .validate_channel(channel_str);
    
    // basic checks
    if (!(channel in channels))
        throw(~perm, "That channel doesn't exist");
    if ((sender() != (channel_owner[channel])) && (!(.is_writable_by(sender()))))
        throw(~perm, "You are not the owner of the channel");
    
    // get the settings
    allowed = [@args, 1][1];
    senders = [@args, 1, 1][2];
    listeners = [@args, [sender()], [sender()], [sender()]][3];
    if ((type(senders) == 'symbol) || (type(listeners) == 'symbol))
        throw(~type, "Cannot use symbols");
    
    // send it off
    ._set_channel(channel, allowed, senders, sender(), listeners);
.

method _del_user_from_channel
    arg channel, who;
    var data;
    
    if (sender() != this())
        throw(~perm, "Sender is not this");
    listeners = dict_add(listeners, channel, setremove(listeners[channel], who));
.

method _add_user_to_channel
    arg channel, who;
    var data;
    
    if (sender() != this())
        throw(~perm, "Sender is not this");
    listeners = dict_add(listeners, channel, (listeners[channel]) + [who]);
.

method tune_out
    arg channel;
    
    if (!(sender() in (listeners[channel])))
        throw(~putz, "You are not tuned into that channel.");
    ._del_user_from_channel(channel, sender());
.

method tune_in
    arg channel;
    
    if (!(channel in channels))
        throw(~chnlnf, "Channel is not available, create it first");
    if (!(.allow_tune_in(channel, sender())))
        throw(~perm, "That is a restricted channel.");
    if (sender() in (listeners[channel]))
        throw(~putz, "You are already tuned into that channel");
    ._add_user_to_channel(channel, sender());
.

method __connected
    return $user_db.connected();
.

method _del_channel
    arg channel;
    
    if (sender() != this())
        throw(~perm, "Sender is not this");
    tuners = dict_del(tuners, channel);
    channels = setremove(channels, channel);
    senders = dict_del(senders, channel);
    channel_owner = dict_del(channel_owner, channel);
    listeners = dict_del(listeners, channel);
.

method new_channel
    arg channel, allowed, senders, owner, listeners;
    var c;
    
    if (!(.is_writable_by(caller())))
        throw(~perm, "Sender not an owner");
    
    // send channel as a string
    channel = .validate_channel(channel, 'no_search);
    if (channel in channels)
        throw(~perm, "That channel already exists");
    if (((| owners[sender()] |) == max_channels_owned) && (!(.is_writable_by(sender()))))
        throw(~perm, ("You can only have " + tostr(max_channels_owned)) + " channels at a time.");
    ._set_channel(channel, allowed, senders, owner, listeners);
    
    // increment ownership
    c = (owner in dict_keys(owners)) ? (owners[owner]) + 1 | c;
    owners = dict_add(owners, owner, c);
.

method init_channels
    if (sender() != $root)
        throw(~perm, "Sender is not $root.");
    tuners = #[['System, 1], ['all, 0], ['Admins, 1]];
    channels = ['System, 'all, 'Admins];
    senders = #[['System, 'admins], ['all, 'admins], ['Admins, 1]];
    owners = #[[$no_one, 3]];
    channel_owner = #[['System, $no_one], ['all, $no_one], ['Admins, $no_one]];
    listeners = #[['System, []], ['all, 'connected], ['Admins, []]];
    max_channels_owned = 3;
.

method valid_channel
    arg channel;
    
    channel = .validate_channel(channel);
    if (!(channel in channels))
        throw(~chnlnf, "Channel doesn't exist");
    return 1;
.

method all_tuners
    return tuners;
.

method _set_channel
    arg name, tune, send, owner, who;
    
    if (sender() != this())
        throw(~perm, "Sender is not this");
    channels = [@channels, name];
    tuners = dict_add(tuners, name, tune);
    senders = dict_add(senders, name, send);
    channel_owner = dict_add(channel_owner, name, owner);
    listeners = dict_add(listeners, name, who);
.

method announce
    arg channel, text;
    var status, who, person, id, x;
    
    if (type(channel) == 'string)
        channel = .validate_channel(channel);
    if (!(channel in channels))
        throw(~chnlnf, "Channel doesn't exist");
    if (!(.allow_send_to(channel, sender(), caller())))
        throw(~perm, "Sender is not allowed to send to that channel.");
    for person in (.listeners(channel)) {
        id = (("<" + tostr(channel)) + " - ") + (sender().namef());
        id = (id + ((sender() in (.listeners(channel))) ? "" | ((!(sender().has_ancestor($user))) ? "" | " (tuned out)"))) + "> ";
        (| person.tell(id + text) |);
    }
.

method coreify_channels
    (| .perms(caller(), $sys) |);
    channels = ['all, 'chatter, 'System, 'Admins];
    tuners = #[['all, 0], ['chatter, 1], ['System, 1], ['Admins, 1]];
    senders = #[['all, 'admins], ['chatter, 1], ['System, 'admins], ['Admins, 1]];
    owners = #[];
    channel_owner = #[['all, this()], ['chatter, this()], ['System, this()], ['Admins, this()]];
    listeners = #[['all, 'connected], ['chatter, []], ['System, []], ['Admins, []]];
.