/* eslint-disable */
import moment from 'moment';
/*
	Adds migration capabilities. Migrations are defined like:

	Migrations.add({
		up: function() {}, //*required* code to run to migrate upwards
		version: 1, //*required* number to identify migration order
		down: function() {}, //*optional* code to run to migrate downwards
		name: 'Something' //*optional* display name for the migration
	});

	The ordering of migrations is determined by the version you set.

	To run the migrations, set the MIGRATE environment variable to either
	'latest' or the version number you want to migrate to. Optionally, append
	',exit' if you want the migrations to exit the meteor process, e.g if you're
	migrating from a script (remember to pass the --once parameter).

	e.g:
	MIGRATE="latest" mrt # ensure we'll be at the latest version and run the app
	MIGRATE="latest,exit" mrt --once # ensure we'll be at the latest version and exit
	MIGRATE="2,exit" mrt --once # migrate to version 2 and exit

	Note: Migrations will lock ensuring only 1 app can be migrating at once. If
	a migration crashes, the control record in the migrations collection will
	remain locked and at the version it was at previously, however the db could
	be in an inconsistant state.
*/

// since we'll be at version 0 by default, we should have a migration set for it.
var DefaultMigration = {
	version: 0,
	up: function() {
		// @TODO: check if collection "migrations" exist
		// If exists, rename and rerun _migrateTo
	}
};

Migrations = {
	_list: [DefaultMigration],
	options: {
		// false disables logging
		log: true,
		// null or a function
		logger: null,
		// enable/disable info log "already at latest."
		logIfLatest: true,
		// lock will be valid for this amount of minutes
		lockExpiration: 5,
		// retry interval in seconds
		retryInterval: 10,
		// max number of attempts to retry unlock
		maxAttempts: 30,
		// migrations collection name
		collectionName: "migrations"
			// collectionName: "rocketchat_migrations"
	},
	config: function(opts) {
		this.options = _.extend({}, this.options, opts);
	},
}

Migrations._collection = new Mongo.Collection(Migrations.options.collectionName);

/* Create a box around messages for displaying on a console.log */
function makeABox(message, color = 'red') {
	if (!_.isArray(message)) {
		message = message.split("\n");
	}
	let len = _(message).reduce(function(memo, msg) {
		return Math.max(memo, msg.length)
	}, 0) + 4;
	let text = message.map((msg) => {
		return "|" [color] + s.lrpad(msg, len)[color] + "|" [color]
	}).join("\n");
	let topLine = "+" [color] + s.pad('', len, '-')[color] + "+" [color];
	let separator = "|" [color] + s.pad('', len, '') + "|" [color];
	let bottomLine = "+" [color] + s.pad('', len, '-')[color] + "+" [color];
	return `\n${topLine}\n${separator}\n${text}\n${separator}\n${bottomLine}\n`;
}

/*
	Logger factory function. Takes a prefix string and options object
	and uses an injected `logger` if provided, else falls back to
	Meteor's `Log` package.
	Will send a log object to the injected logger, on the following form:
		message: String
		level: String (info, warn, error, debug)
		tag: 'Migrations'
*/
function createLogger(prefix) {
	check(prefix, String);

	// Return noop if logging is disabled.
	if (Migrations.options.log === false) {
		return function() {};
	}

	return function(level, message) {
		check(level, Match.OneOf('info', 'error', 'warn', 'debug'));
		check(message, Match.OneOf(String, [String]));

		var logger = Migrations.options && Migrations.options.logger;

		if (logger && _.isFunction(logger)) {

			logger({
				level: level,
				message: message,
				tag: prefix
			});

		} else {
			Log[level]({
				message: prefix + ': ' + message
			});
		}
	}
}

var log;

var options = Migrations.options;

// collection holding the control record

log = createLogger('Migrations');

['info', 'warn', 'error', 'debug'].forEach(function(level) {
	log[level] = _.partial(log, level);
});

// if (process.env.MIGRATE)
//   Migrations.migrateTo(process.env.MIGRATE);

// Add a new migration:
// {up: function *required
//  version: Number *required
//  down: function *optional
//  name: String *optional
// }
Migrations.add = function(migration) {
	if (typeof migration.up !== 'function')
		throw new Meteor.Error('Migration must supply an up function.');

	if (typeof migration.version !== 'number')
		throw new Meteor.Error('Migration must supply a version number.');

	if (migration.version <= 0)
		throw new Meteor.Error('Migration version must be greater than 0');

	// Freeze the migration object to make it hereafter immutable
	Object.freeze(migration);

	this._list.push(migration);
	this._list = _.sortBy(this._list, function(m) {
		return m.version;
	});
}

// Attempts to run the migrations using command in the form of:
// e.g 'latest', 'latest,exit', 2
// use 'XX,rerun' to re-run the migration at that version
Migrations.migrateTo = function(command) {
	if (_.isUndefined(command) || command === '' || this._list.length === 0)
		throw new Error("Cannot migrate using invalid command: " + command);

	if (typeof command === 'number') {
		var version = command;
	} else {
		var version = command.split(',')[0];
		var subcommand = command.split(',')[1];
	}

	const maxAttempts = Migrations.options.maxAttempts;
	const retryInterval = Migrations.options.retryInterval;
	for (let attempts = 1; attempts <= maxAttempts; attempts++) {
		if (version === 'latest') {
			migrated = this._migrateTo(_.last(this._list).version);
		} else {
			migrated = this._migrateTo(parseInt(version), (subcommand === 'rerun'));
		}
		if (migrated) {
			break;
		} else {
			let willRetry;
			if (attempts < maxAttempts) {
				willRetry = ` Trying again in ${retryInterval} seconds.`;
				Meteor._sleepForMs(retryInterval * 1000);
			} else {
				willRetry = "";
			}
			console.log(`Not migrating, control is locked. Attempt ${attempts}/${maxAttempts}.${willRetry}`.yellow);
		}
	}
	if (!migrated) {
		let control = this._getControl(); // Side effect: upserts control document.
		console.log(makeABox([
			"ERROR! SERVER STOPPED",
			"",
			"Your database migration control is locked.",
			"Please make sure you are running the latest version and try again.",
			"If the problem persists, please contact support.",
			"",
			"This Rocket.Chat version: " + RocketChat.Info.version,
			"Database locked at version: " + control.version,
			"Database target version: " + (version === 'latest' ? _.last(this._list).version : version),
			"",
			"Commit: " + RocketChat.Info.commit.hash,
			"Date: " + RocketChat.Info.commit.date,
			"Branch: " + RocketChat.Info.commit.branch,
			"Tag: " + RocketChat.Info.commit.tag
		]));
		process.exit(1);
	}

	// remember to run meteor with --once otherwise it will restart
	if (subcommand === 'exit')
		process.exit(0);
}

// just returns the current version
Migrations.getVersion = function() {
	return this._getControl().version;
}

// migrates to the specific version passed in
Migrations._migrateTo = function(version, rerun) {
	var self = this;
	var control = this._getControl(); // Side effect: upserts control document.
	var currentVersion = control.version;

	if (lock() === false) {
		// log.info('Not migrating, control is locked.');
		// Warning
		return false;
	}

	if (rerun) {
		log.info('Rerunning version ' + version);
		migrate('up', version);
		log.info('Finished migrating.');
		unlock();
		return true;
	}

	if (currentVersion === version) {
		if (this.options.logIfLatest) {
			log.info('Not migrating, already at version ' + version);
		}
		unlock();
		return true;
	}

	var startIdx = this._findIndexByVersion(currentVersion);
	var endIdx = this._findIndexByVersion(version);

	// log.info('startIdx:' + startIdx + ' endIdx:' + endIdx);
	log.info('Migrating from version ' + this._list[startIdx].version + ' -> ' + this._list[endIdx].version);

	// run the actual migration
	function migrate(direction, idx) {
		var migration = self._list[idx];

		if (typeof migration[direction] !== 'function') {
			unlock();
			throw new Meteor.Error('Cannot migrate ' + direction + ' on version ' + migration.version);
		}

		function maybeName() {
			return migration.name ? ' (' + migration.name + ')' : '';
		}

		log.info('Running ' + direction + '() on version ' + migration.version + maybeName());

		try {
			RocketChat.models._CacheControl.withValue(false, function() {
				migration[direction](migration);
			});
		} catch (e) {
			console.log(makeABox([
				"ERROR! SERVER STOPPED",
				"",
				"Your database migration failed:",
				e.message,
				"",
				"Please make sure you are running the latest version and try again.",
				"If the problem persists, please contact support.",
				"",
				"This Rocket.Chat version: " + RocketChat.Info.version,
				"Database locked at version: " + control.version,
				"Database target version: " + version,
				"",
				"Commit: " + RocketChat.Info.commit.hash,
				"Date: " + RocketChat.Info.commit.date,
				"Branch: " + RocketChat.Info.commit.branch,
				"Tag: " + RocketChat.Info.commit.tag
			]));
			process.exit(1);
		}
	}

	// Returns true if lock was acquired.
	function lock() {
		const date = new Date();
		const dateMinusInterval = moment(date).subtract(self.options.lockExpiration, 'minutes').toDate();
		const build = RocketChat.Info ? RocketChat.Info.build.date : date;

		// This is atomic. The selector ensures only one caller at a time will see
		// the unlocked control, and locking occurs in the same update's modifier.
		// All other simultaneous callers will get false back from the update.
		return self._collection.update({
			_id: 'control',
			$or: [{
				locked: false
			}, {
				lockedAt: {
					$lt: dateMinusInterval
				}
			}, {
				buildAt: {
					$ne: build
				}
			}]
		}, {
			$set: {
				locked: true,
				lockedAt: date,
				buildAt: build
			}
		}) === 1;
	}

	// Side effect: saves version.
	function unlock() {
		self._setControl({
			locked: false,
			version: currentVersion
		});
	}

	if (currentVersion < version) {
		for (var i = startIdx; i < endIdx; i++) {
			migrate('up', i + 1);
			currentVersion = self._list[i + 1].version;
			self._setControl({
				locked: true,
				version: currentVersion
			});
		}
	} else {
		for (var i = startIdx; i > endIdx; i--) {
			migrate('down', i);
			currentVersion = self._list[i - 1].version;
			self._setControl({
				locked: true,
				version: currentVersion
			});
		}
	}

	unlock();
	log.info('Finished migrating.');
}

// gets the current control record, optionally creating it if non-existant
Migrations._getControl = function() {
	var control = this._collection.findOne({
		_id: 'control'
	});

	return control || this._setControl({
		version: 0,
		locked: false
	});
}

// sets the control record
Migrations._setControl = function(control) {
	// be quite strict
	check(control.version, Number);
	check(control.locked, Boolean);

	this._collection.update({
		_id: 'control'
	}, {
		$set: {
			version: control.version,
			locked: control.locked
		}
	}, {
		upsert: true
	});

	return control;
}

// returns the migration index in _list or throws if not found
Migrations._findIndexByVersion = function(version) {
	for (var i = 0; i < this._list.length; i++) {
		if (this._list[i].version === version)
			return i;
	}

	throw new Meteor.Error('Can\'t find migration version ' + version);
}

//reset (mainly intended for tests)
Migrations._reset = function() {
	this._list = [{
		version: 0,
		up: function() {}
	}];
	this._collection.remove({});
}

RocketChat.Migrations = Migrations;


return RocketChat.models.Users.updateLivechatDataByToken(token, key, value);

keep keep replace keep keep keep keep keep

return RocketChat.models.Users.updateLivechatDataByToken(token, key, value, overwrite);

/**
 * Gets visitor by token
 * @param {string} token - Visitor token
 */
RocketChat.models.Rooms.updateSurveyFeedbackById = function(_id, surveyFeedback) {
	const query = {
		_id: _id
	};

	const update = {
		$set: {
			surveyFeedback: surveyFeedback
		}
	};

	return this.update(query, update);
};

RocketChat.models.Rooms.updateLivechatDataByToken = function(token, key, value) {
	const query = {
		'v.token': token,
		open: true
	};

	const update = {
		$set: {
			[`livechatData.${key}`]: value
		}
	};

	return this.update(query, update);
};

RocketChat.models.Rooms.findLivechat = function(filter = {}, offset = 0, limit = 20) {
	const query = _.extend(filter, {
		t: 'l'
	});

	return this.find(query, { sort: { ts: - 1 }, offset: offset, limit: limit });
};

RocketChat.models.Rooms.findLivechatByCode = function(code, fields) {
	code = parseInt(code);

	const options = {};

	if (fields) {
		options.fields = fields;
	}

	// if (this.useCache) {
	// 	return this.cache.findByIndex('t,code', ['l', code], options).fetch();
	// }

	const query = {
		t: 'l',
		code: code
	};

	return this.findOne(query, options);
};

/**
 * Get the next visitor name
 * @return {string} The next visitor name
 */
RocketChat.models.Rooms.getNextLivechatRoomCode = function() {
	const settingsRaw = RocketChat.models.Settings.model.rawCollection();
	const findAndModify = Meteor.wrapAsync(settingsRaw.findAndModify, settingsRaw);

	const query = {
		_id: 'Livechat_Room_Count'
	};

	const update = {
		$inc: {
			value: 1
		}
	};

	const livechatCount = findAndModify(query, null, update);

	return livechatCount.value.value;
};

RocketChat.models.Rooms.findOpenByVisitorToken = function(visitorToken, options) {
	const query = {
		open: true,
		'v.token': visitorToken
	};

	return this.find(query, options);
};

RocketChat.models.Rooms.findByVisitorToken = function(visitorToken) {
	const query = {
		'v.token': visitorToken
	};

	return this.find(query);
};

RocketChat.models.Rooms.findByVisitorId = function(visitorId) {
	const query = {
		'v._id': visitorId
	};

	return this.find(query);
};

RocketChat.models.Rooms.findOneOpenByVisitorId = function(visitorId) {
	const query = {
		open: true,
		'v._id': visitorId
	};

	return this.findOne(query);
};

RocketChat.models.Rooms.setResponseByRoomId = function(roomId, response) {
	return this.update({
		_id: roomId
	}, {
		$set: {
			responseBy: {
				_id: response.user._id,
				username: response.user.username
			},
			responseDate: response.responseDate,
			responseTime: response.responseTime
		},
		$unset: {
			waitingResponse: 1
		}
	});
};

RocketChat.models.Rooms.closeByRoomId = function(roomId, closeInfo) {
	return this.update({
		_id: roomId
	}, {
		$set: {
			closedBy: {
				_id: closeInfo.user._id,
				username: closeInfo.user.username
			},
			closedAt: closeInfo.closedAt,
			chatDuration: closeInfo.chatDuration
		},
		$unset: {
			open: 1
		}
	});
};

RocketChat.models.Rooms.setLabelByRoomId = function(roomId, label) {
	return this.update({ _id: roomId }, { $set: { label: label } });
};

RocketChat.models.Rooms.findOpenByAgent = function(userId) {
	const query = {
		open: true,
		'servedBy._id': userId
	};

	return this.find(query);
};

RocketChat.models.Rooms.changeAgentByRoomId = function(roomId, newAgent) {
	const query = {
		_id: roomId
	};
	const update = {
		$set: {
			servedBy: {
				_id: newAgent.agentId,
				username: newAgent.username
			}
		}
	};

	this.update(query, update);
};

RocketChat.models.Rooms.saveCRMDataByRoomId = function(roomId, crmData) {
	const query = {
		_id: roomId
	};
	const update = {
		$set: {
			crmData
		}
	};

	return this.update(query, update);
};


Template.loginHeader.helpers
	logoUrl: ->
		asset = RocketChat.settings.get('Assets_logo')
		prefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''
		if asset?
			return prefix + (asset.url or asset.defaultUrl)


(function () {
  function CustomEvent ( event, params ) {
    params = params || { bubbles: false, cancelable: false, detail: undefined };
    var evt = document.createEvent( 'CustomEvent' );
    evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
    return evt;
   }

  CustomEvent.prototype = window.Event.prototype;

  window.CustomEvent = CustomEvent;
})();


Package.describe({
	name: 'rocketchat:tutum',
	version: '0.0.1',
	summary: 'RocketChat tutum integration'
});

Package.onUse(function(api) {
	api.use('ecmascript');
	api.use('coffeescript');
	api.use('rocketchat:lib');

	api.addFiles('startup.coffee', 'server');
});

Npm.depends({
	'redis': '2.2.5'
});


RocketChat.models.Rooms.updateLivechatDataByToken = function(token, key, value) {

keep keep keep keep replace keep keep keep

RocketChat.models.Rooms.updateLivechatDataByToken = function(token, key, value, overwrite = true) {

this.CustomFields = (function() {
	var queue = {};
	var initiated = false;

	var setCustomField = function(token, key, value) {
		if (!initiated) {
			// queue by key
			queue[key] = { token, value };
			return;
		}
		Meteor.call('livechat:setCustomField', token, key, value);
	};

	var init = function() {
		Tracker.autorun(function() {
			if (Meteor.userId()) {
				initiated = true;
				Object.keys(queue).forEach((key) => {
					setCustomField.call(this, queue[key].token, key, queue[key].value);
				});
				queue = {};
			} else {
				initiated = false;
			}
		});
	};

	return {
		init: init,
		setCustomField: setCustomField
	};
}());


Meteor.publish('livechat:visitorHistory', function({ rid: roomId }) {
	if (!this.userId) {
		return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:visitorHistory' }));
	}

	if (!RocketChat.authz.hasPermission(this.userId, 'view-l-room')) {
		return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:visitorHistory' }));
	}

	var room = RocketChat.models.Rooms.findOneById(roomId);

	const user = RocketChat.models.Users.findOneById(this.userId);

	if (room.usernames.indexOf(user.username) === -1) {
		return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:visitorHistory' }));
	}

	if (room && room.v && room.v._id) {
		// CACHE: can we stop using publications here?
		return RocketChat.models.Rooms.findByVisitorId(room.v._id);
	} else {
		return this.ready();
	}
});


//! moment.js locale configuration
//! locale : Sinhalese [si]
//! author : Sampath Sitinamaluwa : https://github.com/sampathsris

;(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined'
       && typeof require === 'function' ? factory(require('../moment')) :
   typeof define === 'function' && define.amd ? define(['../moment'], factory) :
   factory(global.moment)
}(this, function (moment) { 'use strict';

    /*jshint -W100*/
    var si = moment.defineLocale('si', {
        months : 'ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්'.split('_'),
        monthsShort : 'ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ'.split('_'),
        weekdays : 'ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා'.split('_'),
        weekdaysShort : 'ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන'.split('_'),
        weekdaysMin : 'ඉ_ස_අ_බ_බ්‍ර_සි_සෙ'.split('_'),
        weekdaysParseExact : true,
        longDateFormat : {
            LT : 'a h:mm',
            LTS : 'a h:mm:ss',
            L : 'YYYY/MM/DD',
            LL : 'YYYY MMMM D',
            LLL : 'YYYY MMMM D, a h:mm',
            LLLL : 'YYYY MMMM D [වැනි] dddd, a h:mm:ss'
        },
        calendar : {
            sameDay : '[අද] LT[ට]',
            nextDay : '[හෙට] LT[ට]',
            nextWeek : 'dddd LT[ට]',
            lastDay : '[ඊයේ] LT[ට]',
            lastWeek : '[පසුගිය] dddd LT[ට]',
            sameElse : 'L'
        },
        relativeTime : {
            future : '%sකින්',
            past : '%sකට පෙර',
            s : 'තත්පර කිහිපය',
            m : 'මිනිත්තුව',
            mm : 'මිනිත්තු %d',
            h : 'පැය',
            hh : 'පැය %d',
            d : 'දිනය',
            dd : 'දින %d',
            M : 'මාසය',
            MM : 'මාස %d',
            y : 'වසර',
            yy : 'වසර %d'
        },
        ordinalParse: /\d{1,2} වැනි/,
        ordinal : function (number) {
            return number + ' වැනි';
        },
        meridiemParse : /පෙර වරු|පස් වරු|පෙ.ව|ප.ව./,
        isPM : function (input) {
            return input === 'ප.ව.' || input === 'පස් වරු';
        },
        meridiem : function (hours, minutes, isLower) {
            if (hours > 11) {
                return isLower ? 'ප.ව.' : 'පස් වරු';
            } else {
                return isLower ? 'පෙ.ව.' : 'පෙර වරු';
            }
        }
    });

    return si;

}));


//! moment.js locale configuration
//! locale : English (United Kingdom) [en-gb]
//! author : Chris Gedrim : https://github.com/chrisgedrim

;(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined'
       && typeof require === 'function' ? factory(require('../moment')) :
   typeof define === 'function' && define.amd ? define(['../moment'], factory) :
   factory(global.moment)
}(this, function (moment) { 'use strict';

    var en_gb = moment.defineLocale('en-gb', {
        months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
        monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
        weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
        weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
        weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
        longDateFormat : {
            LT : 'HH:mm',
            LTS : 'HH:mm:ss',
            L : 'DD/MM/YYYY',
            LL : 'D MMMM YYYY',
            LLL : 'D MMMM YYYY HH:mm',
            LLLL : 'dddd, D MMMM YYYY HH:mm'
        },
        calendar : {
            sameDay : '[Today at] LT',
            nextDay : '[Tomorrow at] LT',
            nextWeek : 'dddd [at] LT',
            lastDay : '[Yesterday at] LT',
            lastWeek : '[Last] dddd [at] LT',
            sameElse : 'L'
        },
        relativeTime : {
            future : 'in %s',
            past : '%s ago',
            s : 'a few seconds',
            m : 'a minute',
            mm : '%d minutes',
            h : 'an hour',
            hh : '%d hours',
            d : 'a day',
            dd : '%d days',
            M : 'a month',
            MM : '%d months',
            y : 'a year',
            yy : '%d years'
        },
        ordinalParse: /\d{1,2}(st|nd|rd|th)/,
        ordinal : function (number) {
            var b = number % 10,
                output = (~~(number % 100 / 10) === 1) ? 'th' :
                (b === 1) ? 'st' :
                (b === 2) ? 'nd' :
                (b === 3) ? 'rd' : 'th';
            return number + output;
        },
        week : {
            dow : 1, // Monday is the first day of the week.
            doy : 4  // The week that contains Jan 4th is the first week of the year.
        }
    });

    return en_gb;

}));


const query = {
'v.token': token,
open: true
};
const update = {
$set: {
[`livechatData.${key}`]: value

const room = this.findOne(query, { fields: { livechatData: 1 } });

if (room.livechatData && typeof room.livechatData[key] !== 'undefined') {


RocketChat.saveRoomTopic = (rid, roomTopic, user, sendMessage=true) ->
	unless Match.test rid, String
		throw new Meteor.Error 'invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomTopic' }

	roomTopic = s.escapeHTML(roomTopic)

	update = RocketChat.models.Rooms.setTopicById(rid, roomTopic)

	if update and sendMessage
		RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_topic', rid, roomTopic, user

	return update


Meteor.methods({
	'livechat:removeAgent'(username) {
		if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'view-livechat-manager')) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:removeAgent' });
		}

		return RocketChat.Livechat.removeAgent(username);
	}
});


Meteor.methods({
	setUserActiveStatus(userId, active) {
		Meteor.users.update(userId, { $set: { active: active } });
		return true;
	}
});


RocketChat.models.Users.updateLivechatDataByToken = function(token, key, value) {

keep keep keep replace keep keep keep keep

RocketChat.models.Users.updateLivechatDataByToken = function(token, key, value, overwrite = true) {


/* globals emojisByCategory:true, emojiCategories:true, toneList:true */

/*
 * Mapping category hashes into human readable and translated names
 */
emojiCategories = {
	people: TAPi18n.__('Smileys_and_People'),
	nature: TAPi18n.__('Animals_and_Nature'),
	food: TAPi18n.__('Food_and_Drink'),
	activity: TAPi18n.__('Activity'),
	travel: TAPi18n.__('Travel_and_Places'),
	objects: TAPi18n.__('Objects'),
	symbols: TAPi18n.__('Symbols'),
	flags: TAPi18n.__('Flags')
};

toneList = {
	'raised_hands': 1,
	'clap': 1,
	'wave': 1,
	'thumbsup': 1,
	'thumbsdown': 1,
	'punch': 1,
	'fist': 1,
	'v': 1,
	'ok_hand': 1,
	'raised_hand': 1,
	'open_hands': 1,
	'muscle': 1,
	'pray': 1,
	'point_up': 1,
	'point_up_2': 1,
	'point_down': 1,
	'point_left': 1,
	'point_right': 1,
	'middle_finger': 1,
	'hand_splayed': 1,
	'metal': 1,
	'vulcan': 1,
	'writing_hand': 1,
	'nail_care': 1,
	'ear': 1,
	'nose': 1,
	'baby': 1,
	'boy': 1,
	'girl': 1,
	'man': 1,
	'woman': 1,
	'person_with_blond_hair': 1,
	'older_man': 1,
	'older_woman': 1,
	'man_with_gua_pi_mao': 1,
	'man_with_turban': 1,
	'cop': 1,
	'construction_worker': 1,
	'guardsman': 1,
	'santa': 1,
	'angel': 1,
	'princess': 1,
	'bride_with_veil': 1,
	'walking': 1,
	'runner': 1,
	'dancer': 1,
	'bow': 1,
	'information_desk_person': 1,
	'no_good': 1,
	'ok_woman': 1,
	'raising_hand': 1,
	'person_with_pouting_face': 1,
	'person_frowning': 1,
	'haircut': 1,
	'massage': 1,
	'rowboat': 1,
	'swimmer': 1,
	'surfer': 1,
	'bath': 1,
	'basketball_player': 1,
	'lifter': 1,
	'bicyclist': 1,
	'mountain_bicyclist': 1,
	'horse_racing': 1,
	'spy': 1,
	'prince': 1,
	'mrs_claus': 1,
	'man_in_tuxedo': 1,
	'shrug': 1,
	'face_palm': 1,
	'pregnant_woman': 1,
	'man_dancing': 1,
	'selfie': 1,
	'fingers_crossed': 1,
	'call_me': 1,
	'left_facing_fist': 1,
	'right_facing_fist': 1,
	'raised_back_of_hand': 1,
	'handshake': 1,
	'cartwheel': 1,
	'wrestlers': 1,
	'water_polo': 1,
	'handball': 1,
	'juggling': 1
};

emojisByCategory = {
	'people': [
		'grinning',
		'grimacing',
		'grin',
		'joy',
		'smiley',
		'smile',
		'sweat_smile',
		'laughing',
		'innocent',
		'wink',
		'blush',
		'slight_smile',
		'upside_down',
		'relaxed',
		'yum',
		'relieved',
		'heart_eyes',
		'kissing_heart',
		'kissing',
		'kissing_smiling_eyes',
		'kissing_closed_eyes',
		'stuck_out_tongue_winking_eye',
		'stuck_out_tongue_closed_eyes',
		'stuck_out_tongue',
		'money_mouth',
		'nerd',
		'sunglasses',
		'hugging',
		'smirk',
		'no_mouth',
		'neutral_face',
		'expressionless',
		'unamused',
		'rolling_eyes',
		'thinking',
		'flushed',
		'disappointed',
		'worried',
		'angry',
		'rage',
		'pensive',
		'confused',
		'slight_frown',
		'frowning2',
		'persevere',
		'confounded',
		'tired_face',
		'weary',
		'triumph',
		'open_mouth',
		'scream',
		'fearful',
		'cold_sweat',
		'hushed',
		'frowning',
		'anguished',
		'cry',
		'disappointed_relieved',
		'sleepy',
		'sweat',
		'sob',
		'dizzy_face',
		'astonished',
		'zipper_mouth',
		'mask',
		'thermometer_face',
		'head_bandage',
		'sleeping',
		'zzz',
		'poop',
		'smiling_imp',
		'imp',
		'japanese_ogre',
		'japanese_goblin',
		'skull',
		'ghost',
		'alien',
		'robot',
		'smiley_cat',
		'smile_cat',
		'joy_cat',
		'heart_eyes_cat',
		'smirk_cat',
		'kissing_cat',
		'scream_cat',
		'crying_cat_face',
		'pouting_cat',
		'raised_hands',
		'clap',
		'wave',
		'thumbsup',
		'thumbsdown',
		'punch',
		'fist',
		'v',
		'ok_hand',
		'raised_hand',
		'open_hands',
		'muscle',
		'pray',
		'point_up',
		'point_up_2',
		'point_down',
		'point_left',
		'point_right',
		'middle_finger',
		'hand_splayed',
		'metal',
		'vulcan',
		'writing_hand',
		'nail_care',
		'lips',
		'tongue',
		'ear',
		'nose',
		'eye',
		'eyes',
		'bust_in_silhouette',
		'busts_in_silhouette',
		'speaking_head',
		'baby',
		'boy',
		'girl',
		'man',
		'woman',
		'person_with_blond_hair',
		'older_man',
		'older_woman',
		'man_with_gua_pi_mao',
		'man_with_turban',
		'cop',
		'construction_worker',
		'guardsman',
		'spy',
		'santa',
		'angel',
		'princess',
		'bride_with_veil',
		'walking',
		'runner',
		'dancer',
		'dancers',
		'couple',
		'two_men_holding_hands',
		'two_women_holding_hands',
		'bow',
		'information_desk_person',
		'no_good',
		'ok_woman',
		'raising_hand',
		'person_with_pouting_face',
		'person_frowning',
		'haircut',
		'massage',
		'couple_with_heart',
		'couple_ww',
		'couple_mm',
		'couplekiss',
		'kiss_ww',
		'kiss_mm',
		'family',
		'family_mwg',
		'family_mwgb',
		'family_mwbb',
		'family_mwgg',
		'family_wwb',
		'family_wwg',
		'family_wwgb',
		'family_wwbb',
		'family_wwgg',
		'family_mmb',
		'family_mmg',
		'family_mmgb',
		'family_mmbb',
		'family_mmgg',
		'womans_clothes',
		'shirt',
		'jeans',
		'necktie',
		'dress',
		'bikini',
		'kimono',
		'lipstick',
		'kiss',
		'footprints',
		'high_heel',
		'sandal',
		'boot',
		'mans_shoe',
		'athletic_shoe',
		'womans_hat',
		'tophat',
		'helmet_with_cross',
		'mortar_board',
		'crown',
		'school_satchel',
		'pouch',
		'purse',
		'handbag',
		'briefcase',
		'eyeglasses',
		'dark_sunglasses',
		'ring',
		'closed_umbrella',
		'cowboy',
		'clown',
		'nauseated_face',
		'rofl',
		'drooling_face',
		'lying_face',
		'sneezing_face',
		'prince',
		'man_in_tuxedo',
		'mrs_claus',
		'face_palm',
		'shrug',
		'selfie',
		'man_dancing',
		'call_me',
		'raised_back_of_hand',
		'left_facing_fist',
		'right_facing_fist',
		'handshake',
		'fingers_crossed',
		'pregnant_woman'
	],
	'nature': [
		'dog',
		'cat',
		'mouse',
		'hamster',
		'rabbit',
		'bear',
		'panda_face',
		'koala',
		'tiger',
		'lion_face',
		'cow',
		'pig',
		'pig_nose',
		'frog',
		'octopus',
		'monkey_face',
		'see_no_evil',
		'hear_no_evil',
		'speak_no_evil',
		'monkey',
		'chicken',
		'penguin',
		'bird',
		'baby_chick',
		'hatching_chick',
		'hatched_chick',
		'wolf',
		'boar',
		'horse',
		'unicorn',
		'bee',
		'bug',
		'snail',
		'beetle',
		'ant',
		'spider',
		'scorpion',
		'crab',
		'snake',
		'turtle',
		'tropical_fish',
		'fish',
		'blowfish',
		'dolphin',
		'whale',
		'whale2',
		'crocodile',
		'leopard',
		'tiger2',
		'water_buffalo',
		'ox',
		'cow2',
		'dromedary_camel',
		'camel',
		'elephant',
		'goat',
		'ram',
		'sheep',
		'racehorse',
		'pig2',
		'rat',
		'mouse2',
		'rooster',
		'turkey',
		'dove',
		'dog2',
		'poodle',
		'cat2',
		'rabbit2',
		'chipmunk',
		'feet',
		'dragon',
		'dragon_face',
		'cactus',
		'christmas_tree',
		'evergreen_tree',
		'deciduous_tree',
		'palm_tree',
		'seedling',
		'herb',
		'shamrock',
		'four_leaf_clover',
		'bamboo',
		'tanabata_tree',
		'leaves',
		'fallen_leaf',
		'maple_leaf',
		'ear_of_rice',
		'hibiscus',
		'sunflower',
		'rose',
		'tulip',
		'blossom',
		'cherry_blossom',
		'bouquet',
		'mushroom',
		'chestnut',
		'jack_o_lantern',
		'shell',
		'spider_web',
		'earth_americas',
		'earth_africa',
		'earth_asia',
		'full_moon',
		'waning_gibbous_moon',
		'last_quarter_moon',
		'waning_crescent_moon',
		'new_moon',
		'waxing_crescent_moon',
		'first_quarter_moon',
		'waxing_gibbous_moon',
		'new_moon_with_face',
		'full_moon_with_face',
		'first_quarter_moon_with_face',
		'last_quarter_moon_with_face',
		'sun_with_face',
		'crescent_moon',
		'star',
		'star2',
		'dizzy',
		'sparkles',
		'comet',
		'sunny',
		'white_sun_small_cloud',
		'partly_sunny',
		'white_sun_cloud',
		'white_sun_rain_cloud',
		'cloud',
		'cloud_rain',
		'thunder_cloud_rain',
		'cloud_lightning',
		'zap',
		'fire',
		'boom',
		'snowflake',
		'cloud_snow',
		'snowman2',
		'snowman',
		'wind_blowing_face',
		'dash',
		'cloud_tornado',
		'fog',
		'umbrella2',
		'umbrella',
		'droplet',
		'sweat_drops',
		'ocean',
		'eagle',
		'duck',
		'bat',
		'shark',
		'owl',
		'fox',
		'butterfly',
		'deer',
		'gorilla',
		'lizard',
		'rhino',
		'wilted_rose',
		'shrimp',
		'squid'
	],
	'food': [
		'green_apple',
		'apple',
		'pear',
		'tangerine',
		'lemon',
		'banana',
		'watermelon',
		'grapes',
		'strawberry',
		'melon',
		'cherries',
		'peach',
		'pineapple',
		'tomato',
		'eggplant',
		'hot_pepper',
		'corn',
		'sweet_potato',
		'honey_pot',
		'bread',
		'cheese',
		'poultry_leg',
		'meat_on_bone',
		'fried_shrimp',
		'cooking',
		'hamburger',
		'fries',
		'hotdog',
		'pizza',
		'spaghetti',
		'taco',
		'burrito',
		'ramen',
		'stew',
		'fish_cake',
		'sushi',
		'bento',
		'curry',
		'rice_ball',
		'rice',
		'rice_cracker',
		'oden',
		'dango',
		'shaved_ice',
		'ice_cream',
		'icecream',
		'cake',
		'birthday',
		'custard',
		'candy',
		'lollipop',
		'chocolate_bar',
		'popcorn',
		'doughnut',
		'cookie',
		'beer',
		'beers',
		'wine_glass',
		'cocktail',
		'tropical_drink',
		'champagne',
		'sake',
		'tea',
		'coffee',
		'baby_bottle',
		'fork_and_knife',
		'fork_knife_plate',
		'croissant',
		'avocado',
		'cucumber',
		'bacon',
		'potato',
		'carrot',
		'french_bread',
		'salad',
		'shallow_pan_of_food',
		'stuffed_flatbread',
		'champagne_glass',
		'tumbler_glass',
		'spoon',
		'egg',
		'milk',
		'peanuts',
		'kiwi',
		'pancakes'
	],
	'activity': [
		'soccer',
		'basketball',
		'football',
		'baseball',
		'tennis',
		'volleyball',
		'rugby_football',
		'8ball',
		'golf',
		'golfer',
		'ping_pong',
		'badminton',
		'hockey',
		'field_hockey',
		'cricket',
		'ski',
		'skier',
		'snowboarder',
		'ice_skate',
		'bow_and_arrow',
		'fishing_pole_and_fish',
		'rowboat',
		'swimmer',
		'surfer',
		'bath',
		'basketball_player',
		'lifter',
		'bicyclist',
		'mountain_bicyclist',
		'horse_racing',
		'levitate',
		'trophy',
		'running_shirt_with_sash',
		'medal',
		'military_medal',
		'reminder_ribbon',
		'rosette',
		'ticket',
		'tickets',
		'performing_arts',
		'art',
		'circus_tent',
		'microphone',
		'headphones',
		'musical_score',
		'musical_keyboard',
		'saxophone',
		'trumpet',
		'guitar',
		'violin',
		'clapper',
		'video_game',
		'space_invader',
		'dart',
		'game_die',
		'slot_machine',
		'bowling',
		'cartwheel',
		'juggling',
		'wrestlers',
		'boxing_glove',
		'martial_arts_uniform',
		'water_polo',
		'handball',
		'goal',
		'fencer',
		'first_place',
		'second_place',
		'third_place',
		'drum'
	],
	'travel': [
		'red_car',
		'taxi',
		'blue_car',
		'bus',
		'trolleybus',
		'race_car',
		'police_car',
		'ambulance',
		'fire_engine',
		'minibus',
		'truck',
		'articulated_lorry',
		'tractor',
		'motorcycle',
		'bike',
		'rotating_light',
		'oncoming_police_car',
		'oncoming_bus',
		'oncoming_automobile',
		'oncoming_taxi',
		'aerial_tramway',
		'mountain_cableway',
		'suspension_railway',
		'railway_car',
		'train',
		'monorail',
		'bullettrain_side',
		'bullettrain_front',
		'light_rail',
		'mountain_railway',
		'steam_locomotive',
		'train2',
		'metro',
		'tram',
		'station',
		'helicopter',
		'airplane_small',
		'airplane',
		'airplane_departure',
		'airplane_arriving',
		'sailboat',
		'motorboat',
		'speedboat',
		'ferry',
		'cruise_ship',
		'rocket',
		'satellite_orbital',
		'seat',
		'anchor',
		'construction',
		'fuelpump',
		'busstop',
		'vertical_traffic_light',
		'traffic_light',
		'checkered_flag',
		'ship',
		'ferris_wheel',
		'roller_coaster',
		'carousel_horse',
		'construction_site',
		'foggy',
		'tokyo_tower',
		'factory',
		'fountain',
		'rice_scene',
		'mountain',
		'mountain_snow',
		'mount_fuji',
		'volcano',
		'japan',
		'camping',
		'tent',
		'park',
		'motorway',
		'railway_track',
		'sunrise',
		'sunrise_over_mountains',
		'desert',
		'beach',
		'island',
		'city_sunset',
		'city_dusk',
		'cityscape',
		'night_with_stars',
		'bridge_at_night',
		'milky_way',
		'stars',
		'sparkler',
		'fireworks',
		'rainbow',
		'homes',
		'european_castle',
		'japanese_castle',
		'stadium',
		'statue_of_liberty',
		'house',
		'house_with_garden',
		'house_abandoned',
		'office',
		'department_store',
		'post_office',
		'european_post_office',
		'hospital',
		'bank',
		'hotel',
		'convenience_store',
		'school',
		'love_hotel',
		'wedding',
		'classical_building',
		'church',
		'mosque',
		'synagogue',
		'kaaba',
		'shinto_shrine',
		'shopping_cart',
		'scooter',
		'motor_scooter',
		'canoe'
	],
	'objects': [
		'watch',
		'iphone',
		'calling',
		'computer',
		'keyboard',
		'desktop',
		'printer',
		'mouse_three_button',
		'trackball',
		'joystick',
		'compression',
		'minidisc',
		'floppy_disk',
		'cd',
		'dvd',
		'vhs',
		'camera',
		'camera_with_flash',
		'video_camera',
		'movie_camera',
		'projector',
		'film_frames',
		'telephone_receiver',
		'telephone',
		'pager',
		'fax',
		'tv',
		'radio',
		'microphone2',
		'level_slider',
		'control_knobs',
		'stopwatch',
		'timer',
		'alarm_clock',
		'clock',
		'hourglass_flowing_sand',
		'hourglass',
		'satellite',
		'battery',
		'electric_plug',
		'bulb',
		'flashlight',
		'candle',
		'wastebasket',
		'oil',
		'money_with_wings',
		'dollar',
		'yen',
		'euro',
		'pound',
		'moneybag',
		'credit_card',
		'gem',
		'scales',
		'wrench',
		'hammer',
		'hammer_pick',
		'tools',
		'pick',
		'nut_and_bolt',
		'gear',
		'chains',
		'gun',
		'bomb',
		'knife',
		'dagger',
		'crossed_swords',
		'shield',
		'smoking',
		'skull_crossbones',
		'coffin',
		'urn',
		'amphora',
		'crystal_ball',
		'prayer_beads',
		'barber',
		'alembic',
		'telescope',
		'microscope',
		'hole',
		'pill',
		'syringe',
		'thermometer',
		'label',
		'bookmark',
		'toilet',
		'shower',
		'bathtub',
		'key',
		'key2',
		'couch',
		'sleeping_accommodation',
		'bed',
		'door',
		'bellhop',
		'frame_photo',
		'map',
		'beach_umbrella',
		'moyai',
		'shopping_bags',
		'balloon',
		'flags',
		'ribbon',
		'gift',
		'confetti_ball',
		'tada',
		'dolls',
		'wind_chime',
		'crossed_flags',
		'izakaya_lantern',
		'envelope',
		'envelope_with_arrow',
		'incoming_envelope',
		'e-mail',
		'love_letter',
		'postbox',
		'mailbox_closed',
		'mailbox',
		'mailbox_with_mail',
		'mailbox_with_no_mail',
		'package',
		'postal_horn',
		'inbox_tray',
		'outbox_tray',
		'scroll',
		'page_with_curl',
		'bookmark_tabs',
		'bar_chart',
		'chart_with_upwards_trend',
		'chart_with_downwards_trend',
		'page_facing_up',
		'date',
		'calendar',
		'calendar_spiral',
		'card_index',
		'card_box',
		'ballot_box',
		'file_cabinet',
		'clipboard',
		'notepad_spiral',
		'file_folder',
		'open_file_folder',
		'dividers',
		'newspaper2',
		'newspaper',
		'notebook',
		'closed_book',
		'green_book',
		'blue_book',
		'orange_book',
		'notebook_with_decorative_cover',
		'ledger',
		'books',
		'book',
		'link',
		'paperclip',
		'paperclips',
		'scissors',
		'triangular_ruler',
		'straight_ruler',
		'pushpin',
		'round_pushpin',
		'triangular_flag_on_post',
		'flag_white',
		'flag_black',
		'closed_lock_with_key',
		'lock',
		'unlock',
		'lock_with_ink_pen',
		'pen_ballpoint',
		'pen_fountain',
		'black_nib',
		'pencil',
		'pencil2',
		'crayon',
		'paintbrush',
		'mag',
		'mag_right'
	],
	'symbols': [
		'100',
		'1234',
		'heart',
		'yellow_heart',
		'green_heart',
		'blue_heart',
		'purple_heart',
		'broken_heart',
		'heart_exclamation',
		'two_hearts',
		'revolving_hearts',
		'heartbeat',
		'heartpulse',
		'sparkling_heart',
		'cupid',
		'gift_heart',
		'heart_decoration',
		'peace',
		'cross',
		'star_and_crescent',
		'om_symbol',
		'wheel_of_dharma',
		'star_of_david',
		'six_pointed_star',
		'menorah',
		'yin_yang',
		'orthodox_cross',
		'place_of_worship',
		'ophiuchus',
		'aries',
		'taurus',
		'gemini',
		'cancer',
		'leo',
		'virgo',
		'libra',
		'scorpius',
		'sagittarius',
		'capricorn',
		'aquarius',
		'pisces',
		'id',
		'atom',
		'u7a7a',
		'u5272',
		'radioactive',
		'biohazard',
		'mobile_phone_off',
		'vibration_mode',
		'u6709',
		'u7121',
		'u7533',
		'u55b6',
		'u6708',
		'eight_pointed_black_star',
		'vs',
		'accept',
		'white_flower',
		'ideograph_advantage',
		'secret',
		'congratulations',
		'u5408',
		'u6e80',
		'u7981',
		'a',
		'b',
		'ab',
		'cl',
		'o2',
		'sos',
		'no_entry',
		'name_badge',
		'no_entry_sign',
		'x',
		'o',
		'anger',
		'hotsprings',
		'no_pedestrians',
		'do_not_litter',
		'no_bicycles',
		'non-potable_water',
		'underage',
		'no_mobile_phones',
		'exclamation',
		'grey_exclamation',
		'question',
		'grey_question',
		'bangbang',
		'interrobang',
		'low_brightness',
		'high_brightness',
		'trident',
		'fleur-de-lis',
		'part_alternation_mark',
		'warning',
		'children_crossing',
		'beginner',
		'recycle',
		'u6307',
		'chart',
		'sparkle',
		'eight_spoked_asterisk',
		'negative_squared_cross_mark',
		'white_check_mark',
		'diamond_shape_with_a_dot_inside',
		'cyclone',
		'loop',
		'globe_with_meridians',
		'm',
		'atm',
		'sa',
		'passport_control',
		'customs',
		'baggage_claim',
		'left_luggage',
		'wheelchair',
		'no_smoking',
		'wc',
		'parking',
		'potable_water',
		'mens',
		'womens',
		'baby_symbol',
		'restroom',
		'put_litter_in_its_place',
		'cinema',
		'signal_strength',
		'koko',
		'ng',
		'ok',
		'up',
		'cool',
		'new',
		'free',
		'zero',
		'one',
		'two',
		'three',
		'four',
		'five',
		'six',
		'seven',
		'eight',
		'nine',
		'keycap_ten',
		'arrow_forward',
		'pause_button',
		'play_pause',
		'stop_button',
		'record_button',
		'track_next',
		'track_previous',
		'fast_forward',
		'rewind',
		'twisted_rightwards_arrows',
		'repeat',
		'repeat_one',
		'arrow_backward',
		'arrow_up_small',
		'arrow_down_small',
		'arrow_double_up',
		'arrow_double_down',
		'arrow_right',
		'arrow_left',
		'arrow_up',
		'arrow_down',
		'arrow_upper_right',
		'arrow_lower_right',
		'arrow_lower_left',
		'arrow_upper_left',
		'arrow_up_down',
		'left_right_arrow',
		'arrows_counterclockwise',
		'arrow_right_hook',
		'leftwards_arrow_with_hook',
		'arrow_heading_up',
		'arrow_heading_down',
		'hash',
		'asterisk',
		'information_source',
		'abc',
		'abcd',
		'capital_abcd',
		'symbols',
		'musical_note',
		'notes',
		'wavy_dash',
		'curly_loop',
		'heavy_check_mark',
		'arrows_clockwise',
		'heavy_plus_sign',
		'heavy_minus_sign',
		'heavy_division_sign',
		'heavy_multiplication_x',
		'heavy_dollar_sign',
		'currency_exchange',
		'copyright',
		'registered',
		'tm',
		'end',
		'back',
		'on',
		'top',
		'soon',
		'ballot_box_with_check',
		'radio_button',
		'white_circle',
		'black_circle',
		'red_circle',
		'large_blue_circle',
		'small_orange_diamond',
		'small_blue_diamond',
		'large_orange_diamond',
		'large_blue_diamond',
		'small_red_triangle',
		'black_small_square',
		'white_small_square',
		'black_large_square',
		'white_large_square',
		'small_red_triangle_down',
		'black_medium_square',
		'white_medium_square',
		'black_medium_small_square',
		'white_medium_small_square',
		'black_square_button',
		'white_square_button',
		'speaker',
		'sound',
		'loud_sound',
		'mute',
		'mega',
		'loudspeaker',
		'bell',
		'no_bell',
		'black_joker',
		'mahjong',
		'spades',
		'clubs',
		'hearts',
		'diamonds',
		'flower_playing_cards',
		'thought_balloon',
		'anger_right',
		'speech_balloon',
		'clock1',
		'clock2',
		'clock3',
		'clock4',
		'clock5',
		'clock6',
		'clock7',
		'clock8',
		'clock9',
		'clock10',
		'clock11',
		'clock12',
		'clock130',
		'clock230',
		'clock330',
		'clock430',
		'clock530',
		'clock630',
		'clock730',
		'clock830',
		'clock930',
		'clock1030',
		'clock1130',
		'clock1230',
		'eye_in_speech_bubble',
		'speech_left',
		'eject',
		'black_heart',
		'octagonal_sign',
		'asterisk_symbol',
		'pound_symbol',
		'digit_nine',
		'digit_eight',
		'digit_seven',
		'digit_six',
		'digit_five',
		'digit_four',
		'digit_three',
		'digit_two',
		'digit_one',
		'digit_zero',
		'regional_indicator_z',
		'regional_indicator_y',
		'regional_indicator_x',
		'regional_indicator_w',
		'regional_indicator_v',
		'regional_indicator_u',
		'regional_indicator_t',
		'regional_indicator_s',
		'regional_indicator_r',
		'regional_indicator_q',
		'regional_indicator_p',
		'regional_indicator_o',
		'regional_indicator_n',
		'regional_indicator_m',
		'regional_indicator_l',
		'regional_indicator_k',
		'regional_indicator_j',
		'regional_indicator_i',
		'regional_indicator_h',
		'regional_indicator_g',
		'regional_indicator_f',
		'regional_indicator_e',
		'regional_indicator_d',
		'regional_indicator_c',
		'regional_indicator_b',
		'regional_indicator_a'
	],
	'flags': [
		'flag_ac',
		'flag_af',
		'flag_al',
		'flag_dz',
		'flag_ad',
		'flag_ao',
		'flag_ai',
		'flag_ag',
		'flag_ar',
		'flag_am',
		'flag_aw',
		'flag_au',
		'flag_at',
		'flag_az',
		'flag_bs',
		'flag_bh',
		'flag_bd',
		'flag_bb',
		'flag_by',
		'flag_be',
		'flag_bz',
		'flag_bj',
		'flag_bm',
		'flag_bt',
		'flag_bo',
		'flag_ba',
		'flag_bw',
		'flag_br',
		'flag_bn',
		'flag_bg',
		'flag_bf',
		'flag_bi',
		'flag_cv',
		'flag_kh',
		'flag_cm',
		'flag_ca',
		'flag_ky',
		'flag_cf',
		'flag_td',
		'flag_cl',
		'flag_cn',
		'flag_co',
		'flag_km',
		'flag_cg',
		'flag_cd',
		'flag_cr',
		'flag_hr',
		'flag_cu',
		'flag_cy',
		'flag_cz',
		'flag_dk',
		'flag_dj',
		'flag_dm',
		'flag_do',
		'flag_ec',
		'flag_eg',
		'flag_sv',
		'flag_gq',
		'flag_er',
		'flag_ee',
		'flag_et',
		'flag_fk',
		'flag_fo',
		'flag_fj',
		'flag_fi',
		'flag_fr',
		'flag_pf',
		'flag_ga',
		'flag_gm',
		'flag_ge',
		'flag_de',
		'flag_gh',
		'flag_gi',
		'flag_gr',
		'flag_gl',
		'flag_gd',
		'flag_gu',
		'flag_gt',
		'flag_gn',
		'flag_gw',
		'flag_gy',
		'flag_ht',
		'flag_hn',
		'flag_hk',
		'flag_hu',
		'flag_is',
		'flag_in',
		'flag_id',
		'flag_ir',
		'flag_iq',
		'flag_ie',
		'flag_il',
		'flag_it',
		'flag_ci',
		'flag_jm',
		'flag_jp',
		'flag_je',
		'flag_jo',
		'flag_kz',
		'flag_ke',
		'flag_ki',
		'flag_xk',
		'flag_kw',
		'flag_kg',
		'flag_la',
		'flag_lv',
		'flag_lb',
		'flag_ls',
		'flag_lr',
		'flag_ly',
		'flag_li',
		'flag_lt',
		'flag_lu',
		'flag_mo',
		'flag_mk',
		'flag_mg',
		'flag_mw',
		'flag_my',
		'flag_mv',
		'flag_ml',
		'flag_mt',
		'flag_mh',
		'flag_mr',
		'flag_mu',
		'flag_mx',
		'flag_fm',
		'flag_md',
		'flag_mc',
		'flag_mn',
		'flag_me',
		'flag_ms',
		'flag_ma',
		'flag_mz',
		'flag_mm',
		'flag_na',
		'flag_nr',
		'flag_np',
		'flag_nl',
		'flag_nc',
		'flag_nz',
		'flag_ni',
		'flag_ne',
		'flag_ng',
		'flag_nu',
		'flag_kp',
		'flag_no',
		'flag_om',
		'flag_pk',
		'flag_pw',
		'flag_ps',
		'flag_pa',
		'flag_pg',
		'flag_py',
		'flag_pe',
		'flag_ph',
		'flag_pl',
		'flag_pt',
		'flag_pr',
		'flag_qa',
		'flag_ro',
		'flag_ru',
		'flag_rw',
		'flag_sh',
		'flag_kn',
		'flag_lc',
		'flag_vc',
		'flag_ws',
		'flag_sm',
		'flag_st',
		'flag_sa',
		'flag_sn',
		'flag_rs',
		'flag_sc',
		'flag_sl',
		'flag_sg',
		'flag_sk',
		'flag_si',
		'flag_sb',
		'flag_so',
		'flag_za',
		'flag_kr',
		'flag_es',
		'flag_lk',
		'flag_sd',
		'flag_sr',
		'flag_sz',
		'flag_se',
		'flag_ch',
		'flag_sy',
		'flag_tw',
		'flag_tj',
		'flag_tz',
		'flag_th',
		'flag_tl',
		'flag_tg',
		'flag_to',
		'flag_tt',
		'flag_tn',
		'flag_tr',
		'flag_tm',
		'flag_tv',
		'flag_ug',
		'flag_ua',
		'flag_ae',
		'flag_gb',
		'flag_us',
		'flag_vi',
		'flag_uy',
		'flag_uz',
		'flag_vu',
		'flag_va',
		'flag_ve',
		'flag_vn',
		'flag_wf',
		'flag_eh',
		'flag_ye',
		'flag_zm',
		'flag_zw',
		'flag_re',
		'flag_ax',
		'flag_ta',
		'flag_io',
		'flag_bq',
		'flag_cx',
		'flag_cc',
		'flag_gg',
		'flag_im',
		'flag_yt',
		'flag_nf',
		'flag_pn',
		'flag_bl',
		'flag_pm',
		'flag_gs',
		'flag_tk',
		'flag_bv',
		'flag_hm',
		'flag_sj',
		'flag_um',
		'flag_ic',
		'flag_ea',
		'flag_cp',
		'flag_dg',
		'flag_as',
		'flag_aq',
		'flag_vg',
		'flag_ck',
		'flag_cw',
		'flag_eu',
		'flag_gf',
		'flag_tf',
		'flag_gp',
		'flag_mq',
		'flag_mp',
		'flag_sx',
		'flag_ss',
		'flag_tc',
		'flag_mf'
	]
};

/* exported emojisByCategory, emojiCategories, toneList */


oauth2server = new OAuth2Server
	accessTokensCollectionName: 'rocketchat_oauth_access_tokens'
	refreshTokensCollectionName: 'rocketchat_oauth_refresh_tokens'
	authCodesCollectionName: 'rocketchat_oauth_auth_codes'
	clientsCollection: RocketChat.models.OAuthApps.model
	debug: true

WebApp.connectHandlers.use oauth2server.app

Meteor.publish 'oauthClient', (clientId) ->
	unless @userId
		return @ready()

	return RocketChat.models.OAuthApps.find {clientId: clientId, active: true},
		fields:
			name: 1

RocketChat.API.v1.addAuthMethod ->
	headerToken = @request.headers['authorization']
	getToken = @request.query.access_token

	if headerToken?
		if matches = headerToken.match(/Bearer\s(\S+)/)
			headerToken = matches[1]
		else
			headerToken = undefined

	bearerToken = headerToken or getToken

	if not bearerToken?
		# console.log 'token not found'.red
		return

	# console.log 'bearerToken', bearerToken

	getAccessToken = Meteor.wrapAsync oauth2server.oauth.model.getAccessToken, oauth2server.oauth.model
	accessToken = getAccessToken bearerToken

	if not accessToken?
		# console.log 'accessToken not found'.red
		return

	if accessToken.expires? and accessToken.expires isnt 0 and accessToken.expires < new Date()
		# console.log 'accessToken expired'.red
		return

	user = RocketChat.models.Users.findOne(accessToken.userId)
	if not user?
		# console.log 'user not found'.red
		return

	return user: _.omit(user, '$loki')


import Page from './Page';

class Administration extends Page {
	get flexNav() { return browser.element('.flex-nav'); }
	get flexNavContent() { return browser.element('.flex-nav .content'); }
	get layoutLink() { return browser.element('.flex-nav .content [href="/admin/Layout"]'); }
	get infoLink() { return browser.element('.flex-nav .content [href="/admin/info"]'); }
	get roomsLink() { return browser.element('.flex-nav .content [href="/admin/rooms"]'); }
	get usersLink() { return browser.element('.flex-nav .content [href="/admin/users"]'); }
	get generalLink() { return browser.element('.flex-nav .content [href="/admin/General"]'); }
	get permissionsLink() { return browser.element('.flex-nav .content [href="/admin/permissions"]'); }
	get customScriptBtn() { return browser.element('.section:nth-of-type(6) .collapse'); }
	get customScriptLoggedOutTextArea() { return browser.element('.section:nth-of-type(6) .CodeMirror-scroll'); }
	get customScriptLoggedInTextArea() { return browser.element('.CodeMirror.cm-s-default:nth-of-type(2)'); }
	get infoRocketChatTableTitle() { return browser.element('.content h3'); }
	get infoRocketChatTable() { return browser.element('.content .statistics-table'); }
	get infoCommitTableTitle() { return browser.element('.content h3:nth-of-type(2)'); }
	get infoCommitTable() { return browser.element('.content .statistics-table:nth-of-type(2)'); }
	get infoRuntimeTableTitle() { return browser.element('.content h3:nth-of-type(3)'); }
	get infoRuntimeTable() { return browser.element('.content .statistics-table:nth-of-type(3)'); }
	get infoBuildTableTitle() { return browser.element('.content h3:nth-of-type(4)'); }
	get infoBuildTable() { return browser.element('.content .statistics-table:nth-of-type(4)'); }
	get infoUsageTableTitle() { return browser.element('.content h3:nth-of-type(5)'); }
	get infoUsageTable() { return browser.element('.content .statistics-table:nth-of-type(5)'); }
	get roomsSearchForm() { return browser.element('.content .search'); }
	get roomsFilter() { return browser.element('#rooms-filter'); }
	get roomsChannelsCheckbox() { return browser.element('label:nth-of-type(1) input[name="room-type"]'); }
	get roomsDirectCheckbox() { return browser.element('label:nth-of-type(2) input[name="room-type"]'); }
	get roomsPrivateCheckbox() { return browser.element('label:nth-of-type(3) input[name="room-type"]'); }
	get roomsGeneralChannel() { return browser.element('td=general'); }
	get usersRocketCat() { return browser.element('td=Rocket.Cat'); }
	get usersInternalAdmin() { return browser.element('td=rocketchat.internal.admin.test'); }
	get usersFilter() { return browser.element('#users-filter'); }
	get rolesNewRolesButton() { return browser.element('.button.new-role'); }
	get rolesPermissionGrid() { return browser.element('.permission-grid'); }
	get rolesAdmin() { return browser.element('[title="Admin"]'); }
	get rolesModerator() { return browser.element('[title="Moderator"]'); }
	get rolesOwner() { return browser.element('[title="Owner"]'); }
	get rolesReturnLink() { return browser.element('[href="/admin/permissions"]'); }
	get rolesNewRoleName() { return browser.element('[name="name"]'); }
	get rolesNewRoleDesc() { return browser.element('[name="description"]'); }
	get rolesNewRoleScope() { return browser.element('[name="scope"]'); }
	get rolesAddBtn() { return browser.element('button.add'); }
	get rolesRoomsSearchForm() { return browser.element('.search [name="room"]'); }

	//permissions grids checkboxes

	get rolesUserCreateC() { return browser.element('[name="perm[user][create-c]"]'); }
	get rolesUserCreateP() { return browser.element('[name="perm[user][create-p]"]'); }
	get rolesUserCreateD() { return browser.element('[name="perm[user][create-d]"]'); }
	get rolesUserMentionAll() { return browser.element('[name="perm[user][mention-all]"]'); }
	get rolesUserPreviewC() { return browser.element('[name="perm[user][preview-c-room]"]'); }
	get rolesUserViewC() { return browser.element('[name="perm[user][view-c-room]"]'); }
	get rolesUserViewD() { return browser.element('[name="perm[user][view-d-room]"]'); }
	get rolesUserViewP() { return browser.element('[name="perm[user][view-p-room]"]'); }
	get rolesUserHistory() { return browser.element('[name="perm[user][view-history]"]'); }
	get rolesOwnerDeleteMessage() { return browser.element('[name="perm[owner][delete-message]"]'); }
	get rolesOwnerEditMessage() { return browser.element('[name="perm[owner][edit-message]"]'); }

	get emojiFilter() { return browser.element('#emoji-filter'); }

	//settings
	get generalButtonExpandIframe() { return browser.element('.section:nth-of-type(2) .button.expand'); }
	get generalButtonExpandNotifications() { return browser.element('.section:nth-of-type(3) .button.expand'); }
	get generalButtonExpandRest() { return browser.element('.section:nth-of-type(4) .button.expand'); }
	get generalButtonExpandReporting() { return browser.element('.section:nth-of-type(5) .button.expand'); }
	get generalButtonExpandStreamCast() { return browser.element('.section:nth-of-type(6) .button.expand'); }
	get generalButtonExpandTranslations() { return browser.element('.section:nth-of-type(7) .button.expand'); }
	get generalButtonExpandUTF8() { return browser.element('.section:nth-of-type(8) .button.expand'); }

	get generalSiteUrl() { return browser.element('[name="Site_Url"]'); }
	get generalSiteUrlReset() { return browser.element('.reset-setting[data-setting="Site_Url"]'); }
	get generalSiteName() { return browser.element('[name="Site_Name"]'); }
	get generalSiteNameReset() { return browser.element('.reset-setting[data-setting="Site_Name"]'); }
	get generalLanguage() { return browser.element('[name="Language"]'); }
	get generalLanguagePtOption() { return browser.element('[value="pt"]'); }
	get generalLanguageReset() { return browser.element('.reset-setting[data-setting="Language"]'); }
	get generalSelfSignedCertsTrue() { return browser.element('label:nth-of-type(1) [name="Allow_Invalid_SelfSigned_Certs"]'); }
	get generalSelfSignedCertsFalse() { return browser.element('label:nth-of-type(2) [name="Allow_Invalid_SelfSigned_Certs"]'); }
	get generalSelfSignedCertsReset() { return browser.element('.reset-setting[data-setting="Allow_Invalid_SelfSigned_Certs"]'); }
	get generalFavoriteRoomTrue() { return browser.element('label:nth-of-type(1) [name="Favorite_Rooms"]'); }
	get generalFavoriteRoomFalse() { return browser.element('label:nth-of-type(2) [name="Favorite_Rooms"]'); }
	get generalFavoriteRoomReset() { return browser.element('.reset-setting[data-setting="Favorite_Rooms"]'); }
	get generalCdnPrefix() { return browser.element('[name="CDN_PREFIX"]'); }
	get generalCdnPrefixReset() { return browser.element('.reset-setting[data-setting="CDN_PREFIX"]'); }
	get generalForceSSLTrue() { return browser.element('label:nth-of-type(1) [name="Force_SSL"]'); }
	get generalForceSSLFalse() { return browser.element('label:nth-of-type(2) [name="Force_SSL"]'); }
	get generalForceSSLReset() { return browser.element('.reset-setting[data-setting="Force_SSL"]'); }
	get generalGoogleTagId() { return browser.element('[name="GoogleTagManager_id"]'); }
	get generalGoogleTagIdReset() { return browser.element('.reset-setting[data-setting="GoogleTagManager_id"]'); }
	get generalBugsnagKey() { return browser.element('[name="Bugsnag_api_key"]'); }
	get generalBugsnagKeyReset() { return browser.element('.reset-setting[data-setting="Bugsnag_api_key"]'); }
	get generalIframeSendTrue() { return browser.element('label:nth-of-type(1) [name="Iframe_Integration_send_enable"]'); }
	get generalIframeSendFalse() { return browser.element('label:nth-of-type(2) [name="Iframe_Integration_send_enable"]'); }
	get generalIframeSendReset() { return browser.element('.reset-setting[data-setting="Iframe_Integration_send_enable"]'); }
	get generalIframeSendTargetOrigin() { return browser.element('[name="Iframe_Integration_send_target_origin"]'); }
	get generalIframeSendTargetOriginReset() { return browser.element('.reset-setting[data-setting="Iframe_Integration_send_target_origin"]'); }
	get generalIframeRecieveTrue() { return browser.element('label:nth-of-type(1) [name="Iframe_Integration_receive_enable"]'); }
	get generalIframeRecieveFalse() { return browser.element('label:nth-of-type(2) [name="Iframe_Integration_receive_enable"]'); }
	get generalIframeRecieveFalseReset() { return browser.element('.reset-setting[data-setting="Iframe_Integration_receive_enable"]'); }
	get generalIframeRecieveOrigin() { return browser.element('[name="Iframe_Integration_receive_origin"]'); }
	get generalIframeRecieveOriginReset() { return browser.element('.reset-setting[data-setting="Iframe_Integration_receive_origin"]'); }
	get generalNotificationDuration() { return browser.element('[name="Desktop_Notifications_Duration"]'); }
	get generalNotificationDurationReset() { return browser.element('.reset-setting[data-setting="Desktop_Notifications_Duration"]'); }
	get generalRestApiUserLimit() { return browser.element('[name="API_User_Limit"]'); }
	get generalRestApiUserLimitReset() { return browser.element('.reset-setting[data-setting="API_User_Limit"]'); }
	get generalReportingTrue() { return browser.element('label:nth-of-type(1) [name="Statistics_reporting"]'); }
	get generalReportingFalse() { return browser.element('label:nth-of-type(2) [name="Statistics_reporting"]'); }
	get generalReportingReset() { return browser.element('.reset-setting[data-setting="Statistics_reporting"]'); }
	get generalStreamCastAdress() { return browser.element('[name="Stream_Cast_Address"]'); }
	get generalStreamCastAdressReset() { return browser.element('.reset-setting[data-setting="Stream_Cast_Address"]'); }
	get generalUTF8Regex() { return browser.element('[name="UTF8_Names_Validation"]'); }
	get generalUTF8RegexReset() { return browser.element('.reset-setting[data-setting="UTF8_Names_Validation"]'); }
	get generalUTF8NamesSlugTrue() { return browser.element('label:nth-of-type(1) [name="UTF8_Names_Slugify"]'); }
	get generalUTF8NamesSlugFalse() { return browser.element('label:nth-of-type(2) [name="UTF8_Names_Slugify"]'); }
	get generalUTF8NamesSlugReset() { return browser.element('.reset-setting[data-setting="UTF8_Names_Slugify"]'); }

	checkUserList(user) {
		var element = browser.element('td=adminCreated'+user);
		element.waitForVisible(5000);
		browser.pause(500);
		var result = element.isVisible();
		return result[0];
	}
}

module.exports = new Administration();


const query = {
'profile.token': token
};
const update = {
$set: {
[`livechatData.${key}`]: value
}

const user = this.findOne(query, { fields: { livechatData: 1 } });

if (user.livechatData && typeof user.livechatData[key] !== 'undefined') {

(function(w) {
	w.RocketChat = w.RocketChat || { _: [] };
	var config = {};
	var widget;
	var iframe;
	var hookQueue = [];
	var ready = false;

	var widgetWidth = '320px';
	var widgetHeightOpened = '350px';
	var widgetHeightClosed = '30px';

	// hooks
	var callHook = function(action, params) {
		if (!ready) {
			return hookQueue.push(arguments);
		}
		var data = {
			src: 'rocketchat',
			fn: action,
			args: params
		};
		iframe.contentWindow.postMessage(data, '*');
	};

	var closeWidget = function() {
		widget.dataset.state = 'closed';
		widget.style.height = widgetHeightClosed;
		callHook('widgetClosed');
	};

	var openWidget = function() {
		widget.dataset.state = 'opened';
		widget.style.height = widgetHeightOpened;
		callHook('widgetOpened');
		document.querySelector('.rocketchat-widget iframe').focus();
	};

	var api = {
		ready: function() {
			ready = true;
			if (hookQueue.length > 0) {
				hookQueue.forEach(function(hookParams) {
					callHook.apply(this, hookParams);
				});
				hookQueue = [];
			}
		},
		toggleWindow: function(/*forceClose*/) {
			if (widget.dataset.state === 'closed') {
				openWidget();
			} else {
				closeWidget();
			}
		},
		openPopout: function() {
			closeWidget();
			var popup = window.open(config.url + '?mode=popout', 'livechat-popout', 'width=400, height=450, toolbars=no');
			popup.focus();
		},
		openWidget: function() {
			openWidget();
		},
		removeWidget: function() {
			document.getElementsByTagName('body')[0].removeChild(widget);
		}
	};

	var pageVisited = function(change) {
		callHook('pageVisited', {
			change: change,
			location: JSON.parse(JSON.stringify(document.location)),
			title: document.title
		});
	};

	var setCustomField = function(key, value) {
		callHook('setCustomField', [ key, value ]);
	};

	var setTheme = function(theme) {
		callHook('setTheme', theme);
	};

	var setDepartment = function(department) {
		callHook('setDepartment', department);
	};

	var clearDepartment = function() {
		callHook('clearDepartment');
	};

	var currentPage = {
		href: null,
		title: null
	};
	var trackNavigation = function() {
		setInterval(function() {
			if (document.location.href !== currentPage.href) {
				pageVisited('url');
				currentPage.href = document.location.href;
			}
			if (document.title !== currentPage.title) {
				pageVisited('title');
				currentPage.title = document.title;
			}
		}, 800);
	};

	var init = function(url) {
		if (!url) {
			return;
		}

		config.url = url;

		var chatWidget = document.createElement('div');
		chatWidget.dataset.state = 'closed';
		chatWidget.className = 'rocketchat-widget';
		chatWidget.innerHTML = '
' +
								' '+
								'

';

		chatWidget.style.position = 'fixed';
		chatWidget.style.width = widgetWidth;
		chatWidget.style.height = widgetHeightClosed;
		chatWidget.style.borderTopLeftRadius = '5px';
		chatWidget.style.borderTopRightRadius = '5px';
		chatWidget.style.bottom = '0';
		chatWidget.style.right = '50px';
		chatWidget.style.zIndex = '12345';

		document.getElementsByTagName('body')[0].appendChild(chatWidget);

		widget = document.querySelector('.rocketchat-widget');
		iframe = document.getElementById('rocketchat-iframe');

		w.addEventListener('message', function(msg) {
			if (typeof msg.data === 'object' && msg.data.src !== undefined && msg.data.src === 'rocketchat') {
				if (api[msg.data.fn] !== undefined && typeof api[msg.data.fn] === 'function') {
					var args = [].concat(msg.data.args || []);
					api[msg.data.fn].apply(null, args);
				}
			}
		}, false);

		var mediaqueryresponse = function(mql) {
			if (mql.matches) {
				chatWidget.style.left = '0';
				chatWidget.style.right = '0';
				chatWidget.style.width = '100%';
			} else {
				chatWidget.style.left = 'auto';
				chatWidget.style.right = '50px';
				chatWidget.style.width = widgetWidth;
			}
		};

		var mql = window.matchMedia('screen and (max-device-width: 480px) and (orientation: portrait)');
		mediaqueryresponse(mql);
		mql.addListener(mediaqueryresponse);

		// track user navigation
		trackNavigation();
	};

	if (typeof w.initRocket !== 'undefined') {
		console.warn('initRocket is now deprecated. Please update the livechat code.');
		init(w.initRocket[0]);
	}

	if (typeof w.RocketChat.url !== 'undefined') {
		init(w.RocketChat.url);
	}

	var queue = w.RocketChat._;

	w.RocketChat = w.RocketChat._.push = function(c) {
		c.call(w.RocketChat.livechat);
	};

	// exports
	w.RocketChat.livechat = {
		pageVisited: pageVisited,
		setCustomField: setCustomField,
		setTheme: setTheme,
		setDepartment: setDepartment,
		clearDepartment: clearDepartment
	};

	// proccess queue
	queue.forEach(function(c) {
		c.call(w.RocketChat.livechat);
	});
}(window));


RocketChat.authz.addUserRoles = function(userId, roleNames, scope) {
	if (!userId || !roleNames) {
		return false;
	}

	const user = RocketChat.models.Users.db.findOneById(userId);
	if (!user) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', {
			function: 'RocketChat.authz.addUserRoles'
		});
	}

	roleNames = [].concat(roleNames);
	const existingRoleNames = _.pluck(RocketChat.authz.getRoles(), '_id');
	const invalidRoleNames = _.difference(roleNames, existingRoleNames);

	if (!_.isEmpty(invalidRoleNames)) {
		for (const role of invalidRoleNames) {
			RocketChat.models.Roles.createOrUpdate(role);
		}
	}

	RocketChat.models.Roles.addUserRoles(userId, roleNames, scope);

	return true;
};


Template.mentionsFlexTab.helpers
	hasMessages: ->
		return MentionedMessage.find({ rid: @rid }, { sort: { ts: -1 } }).count() > 0

	messages: ->
		return MentionedMessage.find { rid: @rid }, { sort: { ts: -1 } }

	message: ->
		return _.extend(this, { customClass: 'mentions' })

	hasMore: ->
		return Template.instance().hasMore.get()

Template.mentionsFlexTab.onCreated ->
	@hasMore = new ReactiveVar true
	@limit = new ReactiveVar 50
	@autorun =>
		@subscribe 'mentionedMessages', @data.rid, @limit.get(), =>
			if MentionedMessage.find({ rid: @data.rid }).count() < @limit.get()
				@hasMore.set false

Template.mentionsFlexTab.events
	'click .message-cog': (e, t) ->
		e.stopPropagation()
		e.preventDefault()
		message_id = $(e.currentTarget).closest('.message').attr('id')
		RocketChat.MessageAction.hideDropDown()
		t.$("\##{message_id} .message-dropdown").remove()
		message = MentionedMessage.findOne message_id
		actions = RocketChat.MessageAction.getButtons message, 'mentions'
		el = Blaze.toHTMLWithData Template.messageDropdown, { actions: actions }
		t.$("\##{message_id} .message-cog-container").append el
		dropDown = t.$("\##{message_id} .message-dropdown")
		dropDown.show()

	'scroll .content': _.throttle (e, instance) ->
		if e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight && instance.hasMore.get()
			instance.limit.set(instance.limit.get() + 50)
	, 200


//! moment.js locale configuration
//! locale : English (Canada) [en-ca]
//! author : Jonathan Abourbih : https://github.com/jonbca

;(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined'
       && typeof require === 'function' ? factory(require('../moment')) :
   typeof define === 'function' && define.amd ? define(['../moment'], factory) :
   factory(global.moment)
}(this, function (moment) { 'use strict';

    var en_ca = moment.defineLocale('en-ca', {
        months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
        monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
        weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
        weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
        weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
        longDateFormat : {
            LT : 'h:mm A',
            LTS : 'h:mm:ss A',
            L : 'YYYY-MM-DD',
            LL : 'MMMM D, YYYY',
            LLL : 'MMMM D, YYYY h:mm A',
            LLLL : 'dddd, MMMM D, YYYY h:mm A'
        },
        calendar : {
            sameDay : '[Today at] LT',
            nextDay : '[Tomorrow at] LT',
            nextWeek : 'dddd [at] LT',
            lastDay : '[Yesterday at] LT',
            lastWeek : '[Last] dddd [at] LT',
            sameElse : 'L'
        },
        relativeTime : {
            future : 'in %s',
            past : '%s ago',
            s : 'a few seconds',
            m : 'a minute',
            mm : '%d minutes',
            h : 'an hour',
            hh : '%d hours',
            d : 'a day',
            dd : '%d days',
            M : 'a month',
            MM : '%d months',
            y : 'a year',
            yy : '%d years'
        },
        ordinalParse: /\d{1,2}(st|nd|rd|th)/,
        ordinal : function (number) {
            var b = number % 10,
                output = (~~(number % 100 / 10) === 1) ? 'th' :
                (b === 1) ? 'st' :
                (b === 2) ? 'nd' :
                (b === 3) ? 'rd' : 'th';
            return number + output;
        }
    });

    return en_ca;

}));


import { useTranslation } from '@rocket.chat/ui-contexts';

import type { IMessageSearchProvider } from '@rocket.chat/core-typings';
import { Box, Field, Icon, TextInput, ToggleSwitch } from '@rocket.chat/fuselage';
import { useDebouncedCallback, useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useSetting, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';

type MessageSearchFormProps = {
	provider: IMessageSearchProvider;
	onSearch: (params: { searchText: string; globalSearch: boolean }) => void;
};

const MessageSearchForm = ({ provider, onSearch }: MessageSearchFormProps) => {
	const { handleSubmit, register, setFocus, control } = useForm({
		defaultValues: {
			searchText: '',
			globalSearch: false,
		},
	});

	useEffect(() => {
		setFocus('searchText');
	}, [setFocus]);

	const debouncedOnSearch = useDebouncedCallback(useMutableCallback(onSearch), 300);

	const submitHandler = handleSubmit(({ searchText, globalSearch }) => {
		debouncedOnSearch.cancel();
		onSearch({ searchText, globalSearch });
	});

	const searchText = useWatch({ control, name: 'searchText' });
	const globalSearch = useWatch({ control, name: 'globalSearch' });

	useEffect(() => {
		debouncedOnSearch({ searchText, globalSearch });
	}, [debouncedOnSearch, searchText, globalSearch]);

	const globalSearchEnabled = useSetting<boolean>('GlobalSearchEnabled') ?? false;
	const globalSearchToggleId = useUniqueId();

	const t = useTranslation();

	return (
		<Box
			display='flex'
			flexGrow={0}
			flexShrink={1}
			flexDirection='column'
			p={24}
			borderBlockEndWidth={2}
			borderBlockEndStyle='solid'
			borderBlockEndColor='extra-light'
		>
			<Box is='form' onSubmit={submitHandler}>
				<Field>
					<Field.Row>
						<TextInput
							addon={<Icon name='magnifier' size='x20' />}
							placeholder={t('Search_Messages')}
							aria-label={t('Search_Messages')}
							autoComplete='off'
							{...register('searchText')}
						/>
					</Field.Row>
					{provider.description && <Field.Hint dangerouslySetInnerHTML={{ __html: t(provider.description as TranslationKey) }} />}
				</Field>
				{globalSearchEnabled && (
					<Field>
						<Field.Row>
							<ToggleSwitch id={globalSearchToggleId} {...register('globalSearch')} />
							<Field.Label htmlFor={globalSearchToggleId}>{t('Global_Search')}</Field.Label>
						</Field.Row>
					</Field>
				)}
			</Box>
		</Box>
	);
};

export default MessageSearchForm;


import { Box, ProgressBar, Skeleton } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import { useActiveConnections } from './hooks/useActiveConnections';

const CustomUserActiveConnections = () => {
	const t = useTranslation();

	const result = useActiveConnections();

	if (result.isLoading || result.isError) {
		return (
			<Box w='x180' h='x40' mi='x8' fontScale='c1' display='flex' flexDirection='column' justifyContent='space-around'>
				<Box color='default'>{t('Active_connections')}</Box>
				<Skeleton w='full' />
			</Box>
		);
	}

	const { current, max, percentage } = result.data;

	return (
		<Box w='x180' h='x40' mi='x8' fontScale='c1' display='flex' flexDirection='column' justifyContent='space-around'>
			<Box display='flex' justifyContent='space-between'>
				<Box color='default'>{t('Active_connections')}</Box>
				<Box color='hint'>
					{current}/{max}
				</Box>
			</Box>
			<ProgressBar percentage={percentage} variant={percentage < 80 ? 'success' : 'danger'} />
		</Box>
	);
};

export default CustomUserActiveConnections;


import type * as MessageParser from '@rocket.chat/message-parser';
import { ReactElement, useMemo } from 'react';

import BoldSpan from './BoldSpan';
import ItalicSpan from './ItalicSpan';
import PlainSpan from './PlainSpan';
import StrikeSpan from './StrikeSpan';

const getBaseURI = (): string => {
	if (document.baseURI) {
		return document.baseURI;
	}

	// Should be exactly one tag:
	//   https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
	const base = document.getElementsByTagName('base');

	// Return location from BASE tag.
	if (base.length > 0) {
		return base[0].href;
	}

	// Else use implementation of documentURI:
	//   http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-baseURI
	return document.URL;
};

const isExternal = (href: string): boolean => href.indexOf(getBaseURI()) !== 0;

type LinkSpanProps = {
	href: string;
	label: MessageParser.Markup | MessageParser.Markup[];
};

const LinkSpan = ({ href, label }: LinkSpanProps): ReactElement => {
	const children = useMemo(() => {
		const labelArray = Array.isArray(label) ? label : [label];

		const labelElements = labelArray.map((child, index) => {
			switch (child.type) {
				case 'PLAIN_TEXT':
					return <PlainSpan key={index} text={child.value} />;

				case 'STRIKE':
					return <StrikeSpan key={index} children={child.value} />;

				case 'ITALIC':
					return <ItalicSpan key={index} children={child.value} />;

				case 'BOLD':
					return <BoldSpan key={index} children={child.value} />;

				default:
					return null;
			}
		});

		return labelElements;
	}, [label]);

	if (isExternal(href)) {
		return (

				{children}

		);
	}

	return (

			{children}

	);
};

export default LinkSpan;


import type { ReactiveVar } from 'meteor/reactive-var';
import { useCallback } from 'react';

import { useReactiveValue } from './useReactiveValue';

/** @deprecated */
export const useReactiveVar = <T>(variable: ReactiveVar<T>): T => useReactiveValue(useCallback(() => variable.get(), [variable]));


debouncedOnSearch({ searchText, globalSearch });

}, [debouncedOnSearch, searchText, globalSearch]);

const globalSearchEnabled = useSetting<boolean>('GlobalSearchEnabled') ?? false;

const globalSearchToggleId = useUniqueId();

keep keep keep replace keep keep keep keep keep

const globalSearchEnabled = provider.settings.GlobalSearchEnabled;

import type { ISetting } from '../ISetting';

export interface IMessageSearchProvider {
	key: string;
	description: string;
	icon: string;
	resultTemplate: string;
	supportsSuggestions: boolean;
	suggestionItemTemplate?: string;
	settings: Record<ISetting['_id'], ISetting['value']>;
}


import './ldap';
import './saml';
import './video-conference';


import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';

export const useVisitorInfo = (visitorId: string, { enabled = true, cacheTime = 0 } = {}) => {
	const getVisitorInfo = useEndpoint('GET', '/v1/livechat/visitors.info');
	const { data: { visitor } = {}, ...props } = useQuery(['/v1/livechat/visitors.info', visitorId], () => getVisitorInfo({ visitorId }), {
		enabled,
		cacheTime,
	});
	return { data: visitor, ...props };
};


import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { isUserFederated } from '@rocket.chat/core-typings';

import { hasPermission } from '../../../authorization/server';

Meteor.methods({
	setAdminStatus(userId, admin) {
		check(userId, String);
		check(admin, Match.Optional(Boolean));

		const uid = Meteor.userId();

		if (!uid) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setAdminStatus' });
		}

		if (hasPermission(uid, 'assign-admin-role') !== true) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setAdminStatus' });
		}

		const user = Meteor.users.findOne({ _id: userId }, { fields: { username: 1, federated: 1 } });
		if (!user || isUserFederated(user)) {
			throw new Meteor.Error('error-not-allowed', 'Federated Users cant be admins', { method: 'setAdminStatus' });
		}

		if (admin) {
			return Meteor.call('authorization:addUserToRole', 'admin', user?.username);
		}
		return Meteor.call('authorization:removeUserFromRole', 'admin', user?.username);
	},
});


import type { ISetting } from '../ISetting';

export interface IMessageSearchProvider {


import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo } from 'react';

import { FormSkeleton } from '../../../components/Skeleton';
import { AsyncStatePhase } from '../../../hooks/useAsyncState';
import { useEndpointData } from '../../../hooks/useEndpointData';
import EditDepartment from './EditDepartment';

function EditDepartmentWithAllowedForwardData({ data, ...props }) {
	const t = useTranslation();

	const {
		value: allowedToForwardData,
		phase: allowedToForwardState,
		error: allowedToForwardError,
	} = useEndpointData('/v1/livechat/department.listByIds', {
		params: useMemo(
			() => ({
				ids: data?.department?.departmentsAllowedToForward ?? [],
			}),
			[data],
		),
	});

	if ([allowedToForwardState].includes(AsyncStatePhase.LOADING)) {
		return <FormSkeleton />;
	}

	if (allowedToForwardError) {
		return <Box mbs='x16'>{t('Not_Available')}</Box>;
	}
	return <EditDepartment data={data} allowedToForwardData={allowedToForwardData} {...props} />;
}

export default EditDepartmentWithAllowedForwardData;


import { BlockContext } from '@rocket.chat/ui-kit';
import { action } from '@storybook/addon-actions';

import ButtonElement from '.';
import { parser } from '..';
import Surface from '../Surface';

export default {
	title: 'UiKit/Message/Button element',
	parameters: {
		layout: 'centered',
	},
	decorators: [
		(storyFn) => (
			<Surface
				children={storyFn()}
				dispatchAction={async (payload) => {
					await new Promise((resolve) => setTimeout(resolve, 1000));
					action('dispatchAction')(payload);
				}}
			/>
		),
	],
	argTypes: {
		text: { control: 'object' },
		actionId: { control: 'text' },
		url: { control: 'text' },
		value: { control: 'text' },
		style: { control: { type: 'inline-radio', options: [null, 'primary', 'danger'] } },
		context: { control: { type: 'select', options: BlockContext } },
	},
	args: {
		text: { type: 'plain_text', text: 'Click Me' },
		actionId: undefined,
		url: undefined,
		value: undefined,
		style: null,
		context: undefined,
		confirm: undefined,
	},
};

export const Default = (args) => <ButtonElement {...args} parser={parser} />;
Default.storyName = 'default';


settings: { GlobalSearchEnabled: boolean } & { PageSize: number };


import { Mongo } from 'meteor/mongo';
import type { IRole, IUser } from '@rocket.chat/core-typings';

class UsersCollection extends Mongo.Collection<IUser> {
	constructor() {
		super(null);
	}

	findOneById(uid: IUser['_id'], options: Omit<Mongo.Options<IUser>, 'limit'> = {}) {
		const query: Mongo.Selector<IUser> = {
			_id: uid,
		};

		return this.findOne(query, options);
	}

	isUserInRole(uid: IUser['_id'], roleId: IRole['_id']) {
		const user = this.findOneById(uid, { fields: { roles: 1 } });
		return user && Array.isArray(user.roles) && user.roles.includes(roleId);
	}

	findUsersInRoles(roles: IRole['_id'][] | IRole['_id'], _scope: string, options: any) {
		roles = Array.isArray(roles) ? roles : [roles];

		const query: Mongo.Selector<IUser> = {
			roles: { $in: roles },
		};

		return this.find(query, options);
	}
}

/** @deprecated */
export const Users = new UsersCollection();


import type {
	IMessage,
	IRoom,
	IUser,
	ILivechatDepartment,
	ILivechatPriority,
	IOmnichannelServiceLevelAgreements,
} from '@rocket.chat/core-typings';
import type {
	AggregationCursor,
	CountDocumentsOptions,
	FindCursor,
	FindOptions,
	AggregateOptions,
	InsertOneResult,
	DeleteResult,
} from 'mongodb';

import type { FindPaginated, IBaseModel } from './IBaseModel';

export interface IMessagesModel extends IBaseModel<IMessage> {
	findVisibleByMentionAndRoomId(
		username: IUser['username'],
		rid: IRoom['_id'],
		options: FindOptions<IMessage>,
	): FindPaginated<FindCursor<IMessage>>;

	findStarredByUserAtRoom(userId: IUser['_id'], roomId: IRoom['_id'], options: FindOptions<IMessage>): FindPaginated<FindCursor<IMessage>>;

	findPaginatedByRoomIdAndType(
		roomId: IRoom['_id'],
		type: IMessage['t'],
		options?: FindOptions<IMessage>,
	): FindPaginated<FindCursor<IMessage>>;

	findDiscussionsByRoom(rid: IRoom['_id'], options: FindOptions<IMessage>): FindCursor<IMessage>;

	findDiscussionsByRoomAndText(rid: IRoom['_id'], text: string, options: FindOptions<IMessage>): FindPaginated<FindCursor<IMessage>>;

	findAllNumberOfTransferredRooms(params: {
		start: string;
		end: string;
		departmentId: ILivechatDepartment['_id'];
		onlyCount: boolean;
		options: any;
	}): AggregationCursor<any>;

	getTotalOfMessagesSentByDate(params: { start: Date; end: Date; options?: any }): Promise<any[]>;

	findLivechatClosedMessages(rid: IRoom['_id'], searchTerm?: string, options?: FindOptions<IMessage>): FindPaginated<FindCursor<IMessage>>;
	findLivechatMessages(rid: IRoom['_id'], options?: FindOptions<IMessage>): FindCursor<IMessage>;
	findLivechatMessagesWithoutClosing(rid: IRoom['_id'], options?: FindOptions<IMessage>): FindCursor<IMessage>;
	countRoomsWithStarredMessages(options: AggregateOptions): Promise<number>;

	countRoomsWithPinnedMessages(options: AggregateOptions): Promise<number>;

	findPinned(options: FindOptions<IMessage>): FindCursor<IMessage>;

	findStarred(options: FindOptions<IMessage>): FindCursor<IMessage>;

	setBlocksById(_id: string, blocks: Required<IMessage>['blocks']): Promise<void>;

	addBlocksById(_id: string, blocks: Required<IMessage>['blocks']): Promise<void>;

	countRoomsWithMessageType(type: IMessage['t'], options: AggregateOptions): Promise<number>;

	countByType(type: IMessage['t'], options: CountDocumentsOptions): Promise<number>;

	findPaginatedPinnedByRoom(roomId: IMessage['rid'], options: FindOptions<IMessage>): FindPaginated<FindCursor<IMessage>>;

	setFederationReactionEventId(username: string, _id: string, reaction: string, federationEventId: string): Promise<void>;

	unsetFederationReactionEventId(federationEventId: string, _id: string, reaction: string): Promise<void>;

	findOneByFederationIdAndUsernameOnReactions(federationEventId: string, username: string): Promise<IMessage | null>;

	findOneByFederationId(federationEventId: string): Promise<IMessage | null>;

	setFederationEventIdById(_id: string, federationEventId: string): Promise<void>;

	createPriorityHistoryWithRoomIdMessageAndUser(
		roomId: string,
		user: IMessage['u'],
		priority?: Pick<ILivechatPriority, 'name' | 'i18n'>,
	): Promise<InsertOneResult<IMessage>>;

	createSLAHistoryWithRoomIdMessageAndUser(
		roomId: string,
		user: IMessage['u'],
		sla?: Pick<IOmnichannelServiceLevelAgreements, 'name'>,
	): Promise<InsertOneResult<IMessage>>;

	removeByRoomId(roomId: IRoom['_id']): Promise<DeleteResult>;

	findVisibleByRoomIdNotContainingTypesAndUsers(
		roomId: IRoom['_id'],
		types: IMessage['t'][],
		users?: string[],
		options?: FindOptions<IMessage>,
		showThreadMessages?: boolean,
	): FindCursor<IMessage>;
	findVisibleByRoomIdNotContainingTypesBeforeTs(
		roomId: IRoom['_id'],
		types: IMessage['t'][],
		ts: Date,
		options?: FindOptions<IMessage>,
		showThreadMessages?: boolean,
	): FindCursor<IMessage>;

	findLivechatClosingMessage(rid: IRoom['_id'], options?: FindOptions<IMessage>): Promise<IMessage | null>;
}


import './jumpToMessage';
import './providers';
import { OEmbed } from './server';

export { OEmbed };


keep keep keep replace replace keep keep keep

import debugLib from 'debug'
import fs from 'fs'
import getPort from 'get-port'
import path from 'path'

import { ProviderAliases } from '@prisma/sdk'
import { getSchemaDirSync } from '@prisma/cli'
import { getPlatform } from '@prisma/get-platform'

import { getDatamodelPath } from './utils/getDatamodelPath'

export interface StudioOptions {
  projectDir?: string
  port?: number
}

const debug = debugLib('Studio')
const packageJson = eval(`require('../package.json')`) // tslint:disable-line

export class Studio {
  private projectDir: string
  private instance?: any
  private port?: number

  constructor({ projectDir, port }: StudioOptions = {}) {
    this.projectDir = projectDir || this.getSchemaDir()
    this.port = port
  }

  public async start(providerAliases: ProviderAliases): Promise<string> {
    try {
      if (this.instance) {
        throw new Error(`Studio is already started`)
      }

      const platform = await getPlatform()
      const extension = platform === 'windows' ? '.exe' : ''

      const pathCandidates = [
        // ncc go home
        // tslint:disable-next-line
        eval(`require('path').join(__dirname, '../node_modules/@prisma/sdk/query-engine-${platform}${extension}')`), // for local dev
        // tslint:disable-next-line
        eval(`require('path').join(__dirname, '../query-engine-${platform}${extension}')`), // for production
      ]

      const pathsExist = await Promise.all(
        pathCandidates.map(async candidate => ({ exists: fs.existsSync(candidate), path: candidate })),
      )

      const firstExistingPath = pathsExist.find(p => p.exists)

      if (!firstExistingPath) {
        throw new Error(
          `Could not find any Prisma2 query-engine binary for Studio. Looked in ${pathCandidates.join(', ')}`,
        )
      }

      const StudioServer = (await import('@prisma/studio-server')).default

      let photonWorkerPath: string | undefined
      try {
        const studioTransport = require.resolve('@prisma/studio-transports')
        photonWorkerPath = path.join(path.dirname(studioTransport), 'photon-worker.js')
      } catch (e) {
        //
      }

      if (!this.port) {
        this.port = await getPort({ port: getPort.makeRange(5555, 5600) })
      }

      this.instance = new StudioServer({
        port: this.port,
        debug: false,
        binaryPath: firstExistingPath.path,
        photonWorkerPath,
        photonGenerator: {
          version: packageJson.prisma.version,
          providerAliases,
        },
        schemaPath: getDatamodelPath(this.projectDir),
        // reactAppDir: path.join(path.dirname(require.resolve('@prisma/studio/package.json')), 'build'),
      })

      await this.instance.start()

      return `Studio started at http://localhost:${this.port}`
    } catch (e) {
      debug(e)
    }

    return ''
  }

  public async restart(providerAliases: ProviderAliases): Promise<string> {
    if (this.instance) {
      await this.instance.restart()
      return ''
    }

    return this.start(providerAliases)
  }

  private getSchemaDir(): string {
    const schemaPath = getSchemaDirSync()
    if (!schemaPath) {
      throw new Error(`Could not find schema.prisma`)
    }

    return schemaPath
  }
}


import { Theme } from './types'
import chalk from 'chalk'

// https://www.wnycstudios.org/story/211119-colors
export const gamboge = chalk.rgb(228, 155, 15)

export const darkBrightBlue = chalk.rgb(107, 139, 140)
export const blue = chalk.cyan
export const brightBlue = chalk.rgb(127, 155, 175)
export const identity = (str?: string) => str || ''

export const theme: Theme = {
  keyword: blue,
  entity: blue,
  value: brightBlue,
  punctuation: darkBrightBlue,
  directive: blue,
  function: blue,
  variable: brightBlue,
  string: brightBlue,
  boolean: gamboge,
  comment: chalk.dim,
}