<commit_before>'use strict';

const
    config = require('config'),
    random = require('random-js')(),
    Promise = require('bluebird');

const existingNames = [];
const adjectives = config.get('modules.squads.squad_names.adjectives');
const nouns = config.get('modules.squads.squad_names.nouns');
const textChannelTemplate = config.get('modules.squads.textchannel_template');
const voiceChannelTemplate = config.get('modules.squads.voicechannel_template');
const kickUsersToChannel = config.get('modules.squads.kick_users_to_channel');

class SquadGroup {
    constructor() {
        this._name = null;
        this._leader = null;
        this._members = [];
        this._state = 'closed';
        this._textChannel = null;
        this._voiceChannel = null;
    }

    static fromChannel(channel) {
        if (channel.type !== 'voice') {
            throw new TypeError('channel is not a voice channel');
        }

        const isSquadChannel = this.isSquadChannel(channel);
        if (isSquadChannel.type !== 'voice') {
            // Channel doesn't match
            return null;
        }

        const squad = new SquadGroup();
        squad._name = isSquadChannel.name;
        squad._voiceChannel = channel.id;
        const textChannel = channel.guild.channels.find('name', textChannelTemplate.replace('{name}', this.makeTextChannelName(squad._name)));
        squad._textChannel = textChannel ? textChannel.id : null;
        squad._members = channel.permissionOverwrites.filter(overwrite => overwrite.type === 'member').map(u => u.id);
        squad._leader = squad._members.find(member => {
            const permissions = channel.permissionsFor(member);
            return permissions && permissions.hasPermission('SEND_MESSAGES', true);
        });

        if (!squad._voiceChannel || !squad._textChannel) {
            // We are lacking one of the channels
            // TODO: clean those up
            return null;
        }

        existingNames.push(squad._name);

        return squad;
    }

    static isSquadChannel(channel) {
        const textChannelRegex = new RegExp(`^${textChannelTemplate.replace('{name}', '(.*)')}$`);
        const voiceChannelRegex = new RegExp(`^${voiceChannelTemplate.replace('{name}', '(.*)')}$`);
        let match;
        if (match = channel.name.match(textChannelRegex)) {
            return { type: 'text', name: match[1] };
        } else if (match = channel.name.match(voiceChannelRegex)) {
            return { type: 'voice', name: match[1] };
        }
        return { type: false };
    }

    static generateUniqueName() {
        let name = '';
        let success = false;
        let i = 0;

        // Try generating a name 10 times, if we don't have a unique name after that, give up and generate a numbered room
        do {
            const adjective = random.pick(adjectives);
            const noun = random.pick(nouns);
            name = this.makeVoiceChannelName(`${adjective} ${noun}`);
            success = existingNames.indexOf(name) === -1;
            i++;
        } while (i < 10 && !success);

        if (!success) {
            i = existingNames.length + 1;
            do {
                name = `Squad ${i}`;
                success = existingNames.indexOf(name) === -1;
            } while (!success)
        }

        return name;
    }

    static makeTextChannelName(name) {
        return name.toLowerCase().replace(/\s/g, '-').replace(/'/g, '');
    }

    static makeVoiceChannelName(name) {
        return name.replace(/\w\S*/g, w => `${w.charAt(0).toUpperCase()}${w.substr(1).toLowerCase()}`);
    }

    createChannels(guild) {
        this._name = this.constructor.generateUniqueName();
        existingNames.push(this._name);
        const textChannelName = this.constructor.makeTextChannelName(textChannelTemplate.replace('{name}', this._name));
        const voiceChannelName = voiceChannelTemplate.replace('{name}', this._name);

        return Promise.all([
            guild.createChannel(textChannelName, 'text')
                .then(channel => {
                    return Promise.resolve(channel.overwritePermissions(guild.id, { READ_MESSAGES: false }))
                        .return(channel);
                }),
            guild.createChannel(voiceChannelName, 'voice')
                .then(channel => {
                    return Promise.resolve(channel.overwritePermissions(guild.id, { CONNECT: false }))
                        .return(channel);
                })
        ]).spread((textChannel, voiceChannel) => {
            this._textChannel = textChannel.id;
            this._voiceChannel = voiceChannel.id;
        }).catch(err => {
            existingNames.splice(existingNames.indexOf(this._name), 1);
            throw err;
        });
    }

    deleteChannels(guild) {
        const promises = [];
        if (this._textChannel) {
            promises.push(guild.channels.get(this._textChannel).delete());
        }
        if (this._voiceChannel) {
            promises.push(guild.channels.get(this._voiceChannel).delete());
        }
        return Promise.all(promises).then(() => {
            const index = existingNames.indexOf(this._name);
            if (index > -1) {
                existingNames.splice(index, 1);
            }
        });
    }

    setPermissions(member, permissions) {
        const textChannel = member.guild.channels.get(this._textChannel);
        const voiceChannel = member.guild.channels.get(this._voiceChannel);

        if (!permissions) {
            const textPerm = textChannel.permissionOverwrites.get(member.id);
            const voicePerm = voiceChannel.permissionOverwrites.get(member.id);
            const promises = [];
            if (textPerm) {
                promises.push(textPerm.delete());
            }
            if (voicePerm) {
                promises.push(voicePerm.delete());
            }
            return Promise.all(promises);
        } else {
            return Promise.all([
                textChannel.overwritePermissions(member, permissions),
                voiceChannel.overwritePermissions(member, permissions)
            ]);
        }
    }

    get name() {
        return this._name;
    }

    get leader() {
        return this._leader;
    }

    setLeader(member) {
        if (!member.user) {
            throw new TypeError('member does not have a user associated');
        }

        const oldLeader = member.guild.members.get(this._leader);
        this._leader = member.id;
        if (oldLeader) {
            // Demote or remove previous leader
            let newPermissions = null;
            if (this._members.indexOf(oldLeader.id) > -1) {
                newPermissions = { READ_MESSAGES: true, CONNECT: true };
            }
            return this.setPermissions(oldLeader, newPermissions).then(() => {
                return this.setPermissions(member, { READ_MESSAGES: true, MANAGE_MESSAGES: true, CONNECT: true });
            });<commit_msg>Fix wrong permissions set for a new squad leader<commit_after>'use strict';

const
    config = require('config'),
    random = require('random-js')(),
    Promise = require('bluebird');

const existingNames = [];
const adjectives = config.get('modules.squads.squad_names.adjectives');
const nouns = config.get('modules.squads.squad_names.nouns');
const textChannelTemplate = config.get('modules.squads.textchannel_template');
const voiceChannelTemplate = config.get('modules.squads.voicechannel_template');
const kickUsersToChannel = config.get('modules.squads.kick_users_to_channel');

class SquadGroup {
    constructor() {
        this._name = null;
        this._leader = null;
        this._members = [];
        this._state = 'closed';
        this._textChannel = null;
        this._voiceChannel = null;
    }

    static fromChannel(channel) {
        if (channel.type !== 'voice') {
            throw new TypeError('channel is not a voice channel');
        }

        const isSquadChannel = this.isSquadChannel(channel);
        if (isSquadChannel.type !== 'voice') {
            // Channel doesn't match
            return null;
        }

        const squad = new SquadGroup();
        squad._name = isSquadChannel.name;
        squad._voiceChannel = channel.id;
        const textChannel = channel.guild.channels.find('name', textChannelTemplate.replace('{name}', this.makeTextChannelName(squad._name)));
        squad._textChannel = textChannel ? textChannel.id : null;
        squad._members = channel.permissionOverwrites.filter(overwrite => overwrite.type === 'member').map(u => u.id);
        squad._leader = squad._members.find(member => {
            const permissions = channel.permissionsFor(member);
            return permissions && permissions.hasPermission('SEND_MESSAGES', true);
        });

        if (!squad._voiceChannel || !squad._textChannel) {
            // We are lacking one of the channels
            // TODO: clean those up
            return null;
        }

        existingNames.push(squad._name);

        return squad;
    }

    static isSquadChannel(channel) {
        const textChannelRegex = new RegExp(`^${textChannelTemplate.replace('{name}', '(.*)')}$`);
        const voiceChannelRegex = new RegExp(`^${voiceChannelTemplate.replace('{name}', '(.*)')}$`);
        let match;
        if (match = channel.name.match(textChannelRegex)) {
            return { type: 'text', name: match[1] };
        } else if (match = channel.name.match(voiceChannelRegex)) {
            return { type: 'voice', name: match[1] };
        }
        return { type: false };
    }

    static generateUniqueName() {
        let name = '';
        let success = false;
        let i = 0;

        // Try generating a name 10 times, if we don't have a unique name after that, give up and generate a numbered room
        do {
            const adjective = random.pick(adjectives);
            const noun = random.pick(nouns);
            name = this.makeVoiceChannelName(`${adjective} ${noun}`);
            success = existingNames.indexOf(name) === -1;
            i++;
        } while (i < 10 && !success);

        if (!success) {
            i = existingNames.length + 1;
            do {
                name = `Squad ${i}`;
                success = existingNames.indexOf(name) === -1;
            } while (!success)
        }

        return name;
    }

    static makeTextChannelName(name) {
        return name.toLowerCase().replace(/\s/g, '-').replace(/'/g, '');
    }

    static makeVoiceChannelName(name) {
        return name.replace(/\w\S*/g, w => `${w.charAt(0).toUpperCase()}${w.substr(1).toLowerCase()}`);
    }

    createChannels(guild) {
        this._name = this.constructor.generateUniqueName();
        existingNames.push(this._name);
        const textChannelName = this.constructor.makeTextChannelName(textChannelTemplate.replace('{name}', this._name));
        const voiceChannelName = voiceChannelTemplate.replace('{name}', this._name);

        return Promise.all([
            guild.createChannel(textChannelName, 'text')
                .then(channel => {
                    return Promise.resolve(channel.overwritePermissions(guild.id, { READ_MESSAGES: false }))
                        .return(channel);
                }),
            guild.createChannel(voiceChannelName, 'voice')
                .then(channel => {
                    return Promise.resolve(channel.overwritePermissions(guild.id, { CONNECT: false }))
                        .return(channel);
                })
        ]).spread((textChannel, voiceChannel) => {
            this._textChannel = textChannel.id;
            this._voiceChannel = voiceChannel.id;
        }).catch(err => {
            existingNames.splice(existingNames.indexOf(this._name), 1);
            throw err;
        });
    }

    deleteChannels(guild) {
        const promises = [];
        if (this._textChannel) {
            promises.push(guild.channels.get(this._textChannel).delete());
        }
        if (this._voiceChannel) {
            promises.push(guild.channels.get(this._voiceChannel).delete());
        }
        return Promise.all(promises).then(() => {
            const index = existingNames.indexOf(this._name);
            if (index > -1) {
                existingNames.splice(index, 1);
            }
        });
    }

    setPermissions(member, permissions) {
        const textChannel = member.guild.channels.get(this._textChannel);
        const voiceChannel = member.guild.channels.get(this._voiceChannel);

        if (!permissions) {
            const textPerm = textChannel.permissionOverwrites.get(member.id);
            const voicePerm = voiceChannel.permissionOverwrites.get(member.id);
            const promises = [];
            if (textPerm) {
                promises.push(textPerm.delete());
            }
            if (voicePerm) {
                promises.push(voicePerm.delete());
            }
            return Promise.all(promises);
        } else {
            return Promise.all([
                textChannel.overwritePermissions(member, permissions),
                voiceChannel.overwritePermissions(member, permissions)
            ]);
        }
    }

    get name() {
        return this._name;
    }

    get leader() {
        return this._leader;
    }

    setLeader(member) {
        if (!member.user) {
            throw new TypeError('member does not have a user associated');
        }

        const oldLeader = member.guild.members.get(this._leader);
        this._leader = member.id;
        if (oldLeader) {
            // Demote or remove previous leader
            let newPermissions = null;
            if (this._members.indexOf(oldLeader.id) > -1) {
                newPermissions = { READ_MESSAGES: true, CONNECT: true };
            }
            return this.setPermissions(oldLeader, newPermissions).then(() => {
                return this.setPermissions(member, { READ_MESSAGES: true, SEND_MESSAGES: true, CONNECT: true });
            });
{'fraction_non_alphanumeric': 0.12707694501889752, 'fraction_numerical': 0.0022819653426513586, 'mean_word_length': 2.103341447222837, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6787542', 'n_tokens_mistral': 3854, 'n_tokens_neox': 3771, 'n_words': 748}