<commit_before>var wompt  = require("./includes"),
    constants = wompt.env.constants;
var logger = wompt.logger;

function Channel(config){
	var channel = this;
	
	this.name = config.name;
	this.messages = new wompt.MessageList();
	this.clients = new wompt.ClientPool();
	
	// Called from the context of the client
	this._message_from_client = function(msg){
		msg.from_client = this;
		this.user.touch();
		channel.receive_message(msg);
	}
	
	this._client_disconnected = function(){
		var client = this;
		if(client.user.visible){
			channel.broadcast_user_list_change({
				part: [{
					name: client.user.doc ? client.user.doc.name : 'anonymous',
					id: client.user.doc._id
				}]
			});
		}
	}
}

Channel.prototype = {
	add_client: function(client, token){
		client.meta_data = {
			channel: this,
			token: token
		};

		this.clients.add(client);

		if(client.user.visible){
			this.broadcast_user_list_change({
				join: [{
					name: client.user.doc ? client.user.doc.name : 'anonymous',
					id: client.user.doc._id
				}],
				except: client
			});
		}
		
		client.on('message', this._message_from_client);
		client.on('disconnect', this._client_disconnected);
		
		this.send_initial_data(client);
	},
	
	send_initial_data: function(client){
		client.buffer_sends(function(){
			if(!this.messages.is_empty())
				client.send({action: 'previous', messages: this.messages.recent(10)});
			
			client.send({action: 'who',	users: this.get_user_list(client)});
		}, this);
	},
	
	receive_message: function(data){
		this.action_responders[data.action].call(this, data);
	},
	
	action_responders: {
		post: function(data){
			if(data.from_client.user.readonly) return;
			if(data.msg && data.msg.length > constants.messages.max_length) return;
			var message = {
				action: 'message',
				msg: data.msg,
				from:{
					name: data.from_client.user.doc.name,
					id: data.from_client.user.doc._id
				}
			};
			this.messages.add(message);
			this.broadcast_message(message);
		},
		
		stats: function(data){
			data.from_client.send({
				action: 'stats',
				clients: {
					app: this.app.clients.count,
					channel: this.clients.count,
					me: data.from_client.user.clients.count
				}
			});
		}
	},
	
	get_user_list: function(client){
		var users = [], list = this.clients.list;
		for(var id in list){
			var cl = list[id];
			if(cl.user.visible && cl.user.doc)
				users.push({
					name: cl.user.doc.name || 'anonymous',
					id: cl.user.doc._id
				});
		}
		return users;
	},
	
	broadcast_user_list_change: function(opt){
		var action;
		
		if(opt.part) action='part';
		else if(opt.join) action='join';
		
		this.broadcast_message({
			action:action,
			users: opt.part || opt.join
		}, opt.except);<commit_msg>Only send unique users in the user list when joining a channel<commit_after>var wompt  = require("./includes"),
    constants = wompt.env.constants;
var logger = wompt.logger;

function Channel(config){
	var channel = this;
	
	this.name = config.name;
	this.messages = new wompt.MessageList();
	this.clients = new wompt.ClientPool();
	
	// Called from the context of the client
	this._message_from_client = function(msg){
		msg.from_client = this;
		this.user.touch();
		channel.receive_message(msg);
	}
	
	this._client_disconnected = function(){
		var client = this;
		if(client.user.visible){
			channel.broadcast_user_list_change({
				part: [{
					name: client.user.doc ? client.user.doc.name : 'anonymous',
					id: client.user.doc._id
				}]
			});
		}
	}
}

Channel.prototype = {
	add_client: function(client, token){
		client.meta_data = {
			channel: this,
			token: token
		};

		this.clients.add(client);

		if(client.user.visible){
			this.broadcast_user_list_change({
				join: [{
					name: client.user.doc ? client.user.doc.name : 'anonymous',
					id: client.user.doc._id
				}],
				except: client
			});
		}
		
		client.on('message', this._message_from_client);
		client.on('disconnect', this._client_disconnected);
		
		this.send_initial_data(client);
	},
	
	send_initial_data: function(client){
		client.buffer_sends(function(){
			if(!this.messages.is_empty())
				client.send({action: 'previous', messages: this.messages.recent(10)});
			
			client.send({action: 'who',	users: this.get_user_list(client)});
		}, this);
	},
	
	receive_message: function(data){
		this.action_responders[data.action].call(this, data);
	},
	
	action_responders: {
		post: function(data){
			if(data.from_client.user.readonly) return;
			if(data.msg && data.msg.length > constants.messages.max_length) return;
			var message = {
				action: 'message',
				msg: data.msg,
				from:{
					name: data.from_client.user.doc.name,
					id: data.from_client.user.doc._id
				}
			};
			this.messages.add(message);
			this.broadcast_message(message);
		},
		
		stats: function(data){
			data.from_client.send({
				action: 'stats',
				clients: {
					app: this.app.clients.count,
					channel: this.clients.count,
					me: data.from_client.user.clients.count
				}
			});
		}
	},
	
	get_user_list: function(client){
		var users = {}, list = this.clients.list;
		
		for(var id in list){
			var cl = list[id],
			    doc = cl.user.doc,
			    uid = doc && doc._id;
			
			if(cl.user.visible && doc && !users[uid])
				users[uid]={
					name: doc.name || 'anonymous'
				};
		}
		return users;
	},
	
	broadcast_user_list_change: function(opt){
		var action;
		
		if(opt.part) action='part';
		else if(opt.join) action='join';
		
		this.broadcast_message({
			action:action,
			users: opt.part || opt.join
		}, opt.except);
{'fraction_non_alphanumeric': 0.15280534006855492, 'fraction_numerical': 0.0007216308858019123, 'mean_word_length': 3.281081081081081, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '5286080', 'n_tokens_mistral': 2324, 'n_tokens_neox': 1917, 'n_words': 340}