Phalcon Framework 5.6.1

PDOException: SQLSTATE[HY000] [2002] No existe ninguna ruta hasta el `host'

/srv/ChatHispanoEngine/releases/94/config/services.php (46)
#0PDO->__construct
#1Phalcon\Db\Adapter\Pdo\AbstractPdo->connect
#2Phalcon\Db\Adapter\Pdo\AbstractPdo->__construct
/srv/ChatHispanoEngine/releases/94/config/services.php (46)
<?php
 
$di['config'] = $config;
 
$di['session'] = function () use ($config) {
    $env = trim(file_get_contents(__DIR__.'/environment.txt'));
    $node = trim(file_get_contents(__DIR__.'/node.txt'));
    $path = $config->session->path;
    $manager = new \Phalcon\Session\Manager();
    $serializer = new \Phalcon\Storage\SerializerFactory();
    $factory = new \Phalcon\Storage\AdapterFactory($serializer);
    $o = new \Phalcon\Session\Adapter\Redis($factory, array(
        'host' => '127.0.0.1',
        'port' => 6379,
        'uniqueId' => $config->session->name,
    ));
    $manager->setAdapter($o);
    $manager->start();
 
    return $manager;
};
 
$di['flash'] = function () {
    return new \Phalcon\Flash\Direct(array(
        'error' => 'alert alert-danger alert-dismissible fade show',
        'success' => 'alert alert-success alert-dismissible fade show',
        'notice' => 'alert alert-info alert-dismissible fade show',
        'warning' => 'alert alert-warning alert-dismissible fade show',
    ));
};
 
$di['flashSession'] = function () {
    return new \ChatHispanoEngine\Core\Library\Flash();
};
 
$di['security'] = function () {
    $o = new \Phalcon\Encryption\Security();
    $o->setWorkFactor(12);
 
    return $o;
};
 
$di['db'] = function () use ($config) {
    $node = trim(file_get_contents(__DIR__.'/node.txt'));
    $conf = $config->database->toArray();
    $connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
        'host' => $conf[$node]['host'],
        'username' => $conf[$node]['username'],
        'password' => $conf[$node]['password'],
        'dbname' => $conf[$node]['dbname'],
        'options' => array(
            \PDO::ATTR_CASE => \PDO::CASE_LOWER,
        ),
        'persistent' => true,
    ));
    $connection->execute('SET NAMES '.$conf[$node]['charset']);
 
    return $connection;
};
 
$di['url'] = function () {
    return new \Phalcon\Mvc\Url();
};
 
$di['voltService'] = function ($view) use ($config) {
    $di = $this;
    $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
 
    $cache_dir = $config->view->cache->dir;
    if (!is_dir($cache_dir)) {
        mkdir($cache_dir, 0777, true);
    }
 
    $env = trim(file_get_contents(__DIR__.'/environment.txt'));
    $volt->setOptions(array(
        'path' => $cache_dir,
        'extension' => '.compiled',
        'always' => $env == 'prod' ? false : true,
    ));
 
    $compiler = $volt->getCompiler();
    $compiler->addFunction('assets', function ($resolvedArgs, $exprArgs) use ($di) {
        return $di->get('assets');
    });
 
    $compiler->addFunction('_t', function ($resolvedArgs, $exprArgs) use ($di) {
        return '$this->translate->translate('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('flashSession', function ($resolvedArgs, $exprArgs) use ($di) {
        return '$this->flashSession->output()';
    });
 
    $compiler->addFunction('rand', function ($resolvedArgs, $exprArgs) use ($di) {
        return 'rand('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('isset', 'isset');
 
    $compiler->addFilter('round', function ($resolvedArgs, $exprArgs) {
        return 'round('.$resolvedArgs.', PHP_ROUND_HALF_DOWN)';
    });
 
    $compiler->addFilter('urlencode', function ($resolvedArgs, $exprArgs) {
        return 'urlencode('.$resolvedArgs.')';
    });
 
    $compiler->addFilter('rawurlencode', function ($resolvedArgs, $exprArgs) {
        return 'rawurlencode('.$resolvedArgs.')';
    });
 
    $compiler->addFilter('urldecode', function ($resolvedArgs, $exprArgs) {
        return 'urldecode('.$resolvedArgs.')';
    });
 
    $compiler->addFilter('utf8encode', function ($resolvedArgs, $exprArgs) {
        return 'utf8_encode('.$resolvedArgs.')';
    });
 
    $compiler->addFilter('utf8decode', function ($resolvedArgs, $exprArgs) {
        return 'utf8_decode('.$resolvedArgs.')';
    });
 
    $compiler->addFilter('escapetahead', function ($resolvedArgs, $exprArgs) {
        return 'str_replace(["\'","à","á","è","é","í","ò","ó","ú"], ["&#39;","&agrave;","&aacute;","&egrave;","&eacute;","&iacute;","&ograve;","&oacute;","&uacute;"], '.$resolvedArgs.')';
    });
 
    $compiler->addFunction('isArray', function ($resolvedArgs, $exprArgs) {
        return 'is_array('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('in_array', function ($resolvedArgs, $exprArgs) {
        return 'in_array('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('implode', function ($resolvedArgs, $exprArgs) {
        return 'implode('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('date', function ($resolvedArgs, $exprArgs) {
        return 'date('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('max', function ($resolvedArgs, $exprArgs) {
        return 'max('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('min', function ($resolvedArgs, $exprArgs) {
        return 'min('.$resolvedArgs.')';
    });
 
    $compiler->addFunction('htmlspecialchars', function ($resolvedArgs, $exprArgs) {
        return 'htmlspecialchars('.$resolvedArgs.')';
    });
 
    return $volt;
};
 
$di['auth'] = function () {
    return new \ChatHispanoEngine\Core\Auth\Auth();
};
 
$di['mail'] = function () use ($di) {
    return new \ChatHispanoEngine\Core\Library\Mail(
        $di['redis_cluster']
    );
};
 
$di['acl'] = function () {
    return new \Phalcon\UserPlugin\Acl\Acl();
};
 
$di['cache'] = function () use ($di, $config) {
    return new \Phalcon\Cache\Adapter\Redis(new \Phalcon\Storage\SerializerFactory(), array(
        'host' => '127.0.0.1',
        'port' => 6379,
        'prefix' => $config->session->name.'::',
    ));
};
 
$di['modelsCache'] = function () use ($di, $config) {
    $serializerFactory = new \Phalcon\Storage\SerializerFactory();
    $adapterFactory = new \Phalcon\Cache\AdapterFactory($serializerFactory);
    $options = [
        'defaultSerializer' => 'Php',
        'lifetime'          => 7200
    ];
    $adapter = $adapterFactory->newInstance('apcu', $options);
    return new \Phalcon\Cache\Cache($adapter);
};
 
$di['viewCache'] = $di['cache'];
 
$di['model_manager'] = function () {
    return new \Phalcon\Mvc\Model\Manager();
};
 
$di['translate'] = function () use ($di, $config) {
    $available = $config->i18n->available_languages->toArray();
    $default = $config->i18n->default_locale;
 
    $language = null;
    if (php_sapi_name() != 'cli') {
        $language = $di['session']->get('lang');
    }
    if (null === $language || false === $language || $language == '') {
        /**
         * We try to get the language from http request
         * They are sort from highest priority to low priority
         * so the first one we support is the one we will use.
         * If none is found, the default is taken.
         */
        $found = false;
        if (php_sapi_name() != 'cli') {
            $request = new \Phalcon\Http\Request();
            $langs = $request->getLanguages();
            foreach ($langs as $l) {
                if (isset($available[$l['language']])) {
                    $language = $available[$l['language']];
                    $found = true;
                    break;
                }
            }
        }
        if (false === $found) {
            $language = $default;
        }
    }
 
    if (!file_exists(__DIR__.'/../I18N/'.$language)) {
        $language = $default;
    }
 
    return new \iwalkalone\Translator($available, $language, __DIR__.'/../I18N/', $language, [
        'creg',
        'chan',
        'nick',
        'admin',
        'oper',
        'memo',
        'clones',
        'ipvirtual',
        'docking',
        'datacenter',
        'proxyscanner',
        'garbagecollector',
        'wormhunter',
        'ayuda',
        'errmsg',
        'web',
        'login',
        'shorten',
        'foro',
        'date',
        'bugslayer',
    ]);
};
 
$di['logger'] = function () {
    return new \iwalkalone\Logger();
};
 
$di['oauth2_storage'] = function () {
    return new \ChatHispanoEngine\Core\Library\OAuth2\EmailStorage();
};
 
$di['assets'] = function () {
    return new \Phalcon\Assets\Manager(new \Phalcon\Html\TagFactory(new \Phalcon\Html\Escaper()));
};
 
include 'managers.php';
#3Application->{closure}
#4Phalcon\Di\Service->resolve
#5Phalcon\Di\Di->get
#6Phalcon\Di\Di->getShared
#7Phalcon\Mvc\Model\Manager->getConnection
#8Phalcon\Mvc\Model\Manager->getReadConnection
#9Phalcon\Mvc\Model->getReadConnection
#10Phalcon\Mvc\Model\Query->getReadConnection
#11Phalcon\Mvc\Model\Query->executeSelect
#12Phalcon\Mvc\Model\Query->execute
#13Phalcon\Mvc\Model::find
/srv/ChatHispanoEngine/releases/94/apps/Core/Managers/InspIRCd/ChannelManager.php (4139)
<?php
 
namespace ChatHispanoEngine\Core\Managers\InspIRCd;
 
use ChatHispanoEngine\Core\Library\Cdn\DefaultCdn as Cdn;
use ChatHispanoEngine\Core\Library\Graphics;
use ChatHispanoEngine\Core\Models\User;
use ChatHispanoEngine\Core\Models\Channel\Datasheet;
use ChatHispanoEngine\Core\Models\Channel\Override;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\History;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Forbid;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Section;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Access;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Akick;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Akick\Hit;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Akick\Exception as AkickException;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Akick\Exception\Hit as ExceptionHit;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel\Status;
use ChatHispanoEngine\Core\Models\InspIRCd\Nick\History as NickHistory;
use ChatHispanoEngine\Core\Exception\Exception;
use ChatHispanoEngine\Core\Exception\ErrorCodes;
use Phalcon\Mvc\Model\Transaction\Manager as PhalconTxManager;
 
class ChannelManager extends \Phalcon\Mvc\Model\Manager
{
    public const PAGE_SIZE = 20;
    public const CHANNEL_WARN_EXPIRE_DAYS = 21;
    public const CHANNEL_EXPIRE_DAYS = 30;
    public const REDIS_HASH_CHANNEL_IDENTIFY = 'chathispano_hash_channel_identify';
    public const SUCCESSOR_LEVEL = 499;
    public const CHANNEL_MODES = [
        'C' => ['params' => ['on' => 0, 'off' => 0], ],
        'd' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+$/'], ],
        'E' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^[\*~]{0,1}\d+:\d+(:\d+(:\d+)?)?$/'], ],
        'F' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+:\d+$/'], ],
        'f' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\*{0,1}\d+:\d+$/'], ],
        'H' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+:\d+[yMmdh]{0,1}$/'], ],
        'i' => ['params' => ['on' => 0, 'off' => 0], ],
        'J' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+$/'], ],
        'j' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+:\d+$/'], ],
        'K' => ['params' => ['on' => 0, 'off' => 0], ],
        'k' => ['params' => ['on' => 1, 'off' => 1], ],
        'l' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+$/'], ],
        'M' => ['params' => ['on' => 0, 'off' => 0], ],
        'm' => ['params' => ['on' => 0, 'off' => 0], ],
        'N' => ['params' => ['on' => 0, 'off' => 0], ],
        'n' => ['params' => ['on' => 0, 'off' => 0], ],
        'O' => ['params' => ['on' => 0, 'off' => 0], ],
        'p' => ['params' => ['on' => 0, 'off' => 0], ],
        'Q' => ['params' => ['on' => 0, 'off' => 0], ],
        'R' => ['params' => ['on' => 0, 'off' => 0], ],
        'r' => ['params' => ['on' => 0, 'off' => 0], ],
        'S' => ['params' => ['on' => 0, 'off' => 0], ],
        's' => ['params' => ['on' => 0, 'off' => 0], ],
        'T' => ['params' => ['on' => 0, 'off' => 0], ],
        't' => ['params' => ['on' => 0, 'off' => 0], ],
        'u' => ['params' => ['on' => 0, 'off' => 0], ],
        'W' => ['params' => ['on' => 0, 'off' => 0], ],
        'x' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^[\*~]{0,1}\d+:\d+$/'], ],
        'y' => ['params' => ['on' => 1, 'off' => 0, 'pattern' => '/^\d+:\d+$/'], ],
        'z' => ['params' => ['on' => 0, 'off' => 0], ],
    ];
 
    protected $channel_public_columns = 'name,founder,successor,section,description,is_suspended,suspend_ts,suspend_duration,suspend_reason,time_registered,last_topic,last_topic_by,last_topic_time';
 
    protected $default_levels = [
        'AUTOOP' => 300,
        'AUTOVOICE' => 100,
        'AUTODEOP' => 200,
        'AUTODEVOICE' => 50,
        'NOJOIN' => -1,
        'INVITE' => 300,
        'AKICK-CHANGE' => 500,
        'AKICK-LIST' => 300,
        'SET' => 500,
        'CLEAR' => 500,
        'TOPIC' => 500,
        'UNBAN' => 300,
        'OPDEOP' => 100,
        'VOICEDEVOICE' => 100,
        'ACC-LIST' => 0,
        'ACC-CHANGE' => 500,
        'MEMO-RECV' => 500,
        'MEMO-SEND' => 0,
        'BLOGGER' => 500,
        'FORUM' => 500,
        'IDENTIFY' => 0,
    ];
 
    public function getDefaultLevels()
    {
        return $this->default_levels;
    }
 
    public function listSections($limit = 0)
    {
        if ($limit > 0) {
            return Section::find([
                'order' => 'section_order ASC, code ASC',
                'limit' => $limit,
            ]);
        } else {
            return Section::find([
                'order' => 'section_order ASC, code ASC',
            ]);
        }
    }
 
    public function getSection($code)
    {
        $o = Section::findFirst([
            'code = :code:',
            'bind' => [
                'code' => mb_strtoupper($code),
            ],
        ]);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_NOT_FOUND);
        }
 
        return $o;
    }
 
    public function getSectionBySlug($slug)
    {
        $o = Section::findFirst([
            'slug = :slug:',
            'bind' => [
                'slug' => mb_strtolower($slug),
            ],
        ]);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_NOT_FOUND);
        }
 
        return $o;
    }
 
    protected function checkSectionParameters(array $data)
    {
        if (!isset($data['name']) || strlen($data['name']) == "") {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_CREATE, [
                'messages' => ['Name cannot be empty'],
            ]);
        }
        if (!isset($data['description']) || strlen($data['description']) < 10) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_CREATE, [
                'messages' => ['Description must have 10 characters at least'],
            ]);
        }
    }
 
    public function newSection(array $data)
    {
        if (!isset($data['code']) || strlen($data['code']) != 3) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_CREATE, [
                'messages' => ['Code must be 3 letter string'],
            ]);
        }
        $this->checkSectionParameters($data);
        $data['code'] = mb_strtoupper($data['code']);
 
        $o = new Section();
        $o->assign($data);
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_CREATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function updateSection($code, array $data)
    {
        $o = $this->getSection($code);
        $this->checkSectionParameters($data);
        $o->assign($data);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function deleteSection($code)
    {
        $o = $this->getSection($code);
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_DELETE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function countListBot($pattern, $nick, $ip)
    {
        if (strlen($pattern) > 0) {
            $pattern = str_replace('*', '%', mb_strtolower($pattern));
 
            return Channel::count([
                'name LIKE :pattern:',
                'bind' => [
                    'pattern' => $pattern,
                ],
            ]);
        } else {
            return Channel::count([
                'bind' => [
                    'pattern' => $pattern,
                ],
            ]);
        }
    }
 
    public function getAll()
    {
        return Channel::find();
    }
 
    public function getListBot($pattern, $page, $nick, $ip)
    {
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($nick, 'preoper');
 
        if (strlen($pattern) > 0) {
            $pattern = str_replace('*', '%', mb_strtolower($pattern));
 
            return Channel::find([
                'name LIKE :pattern:',
                'bind' => [
                    'pattern' => $pattern,
                ],
                'order' => 'name ASC',
                'limit' => self::PAGE_SIZE,
                'offset' => ($page - 1) * self::PAGE_SIZE,
            ]);
        } else {
            return Channel::find([
                'order' => 'name ASC',
                'limit' => self::PAGE_SIZE,
                'offset' => ($page - 1) * self::PAGE_SIZE,
            ]);
        }
    }
 
    public function countListGI()
    {
        return Channel::count([
            'ig = 1',
            'order' => 'name ASC',
        ]);
    }
 
    public function getListGIAll()
    {
        return Channel::find([
            'ig = 1',
            'order' => 'name ASC',
        ]);
    }
 
    public function getListGI($page, $nick)
    {
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($nick, 'preoper');
 
        return Channel::find([
            'ig = 1',
            'order' => 'name ASC',
            'limit' => self::PAGE_SIZE,
            'offset' => ($page - 1) * self::PAGE_SIZE,
        ]);
    }
 
    public function count()
    {
        return Channel::count();
    }
 
    public function countByUrl($url)
    {
        return Channel::count([
            'ig = :ig: AND (LOWER(description) like :url: or LOWER(entrymsg) like :url: or LOWER(last_topic) like :url:)',
            'bind' => [
                'ig' => 1,
                'url' => '%'.mb_strtolower($url).'%',
            ]
        ]);
    }
 
    public function listAll()
    {
        return Channel::find();
    }
 
    public function listIg()
    {
        return Channel::find([
            'ig = :ig:',
            'bind' => [
                'ig' => 1,
            ],
        ]);
    }
 
    public function listSuspendExpired()
    {
        return Channel::find([
            'is_suspended = 1 AND suspend_ts + suspend_duration <= :ts:',
            'bind' => [
                'ts' => time(),
            ],
        ]);
    }
 
    public function getList($name, $order = 'name ASC', $from = 0, $num = 100, $full = false)
    {
        if ($full) {
            $columns = '*';
        } else {
            $columns = $this->channel_public_columns;
        }
 
        if (strlen($name) > 0) {
            $conditions = 'LOWER(name) LIKE :name:';
            $bind = [
                'name' => '%'.mb_strtolower($name).'%',
            ];
        } else {
            $conditions = '';
            $bind = [];
        }
 
        if ($conditions == '') {
            return Channel::find(array(
                'columns' => $columns,
                'order' => $order,
                'limit' => $num,
                'offset' => $from,
            ));
        } else {
            return Channel::find(array(
                $conditions,
                'bind' => $bind,
                'columns' => $columns,
                'order' => $order,
                'limit' => $num,
                'offset' => $from,
            ));
        }
    }
 
    public function getImportant()
    {
        $config = $this->getDI()->get('config');
        $redis = $this->getDI()->get('redis_cluster');
        $list = $redis->hGetAll('chathispano_channels_hash_name');
 
        $data = [];
        foreach ($list as $item) {
            $o = json_decode($item, true);
            $cnt = $o['cnt_members'];
            if ($cnt < $config->channels->important->minimum_nicks) {
                continue;
            }
            if (preg_match('/[sp]/', $o['modes'])) {
                continue;
            }
            if (!isset($data[$cnt])) {
                $data[$cnt] = [];
            }
            $data[$cnt][] = [
                'name' => $o['name'],
                'description' => $o['topic'],
                'users' => $o['cnt_members'],
                'important' => 0,
            ];
        }
        krsort($data, SORT_NUMERIC);
 
        $result = [];
        foreach ($data as $cnt => $channels) {
            foreach ($channels as $c) {
                $result[] = $c;
            }
        }
 
        return $result;
    }
 
    public function get($name, $full = false, $cached = false)
    {
        if ($cached === true) {
            $o = Channel::findFirst([
                'columns' => $full ? '*' : $this->channel_public_columns,
                'name = :name:',
                'bind' => [
                    'name' => mb_strtolower($name),
                ],
                'cache' => [
                    'key' => 'inspircd-channel-'.mb_strtolower($name).($full ? '-full' : ''),
                    'lifetime' => 300,
                ],
            ]);
        } else {
            $o = Channel::findFirst([
                'columns' => $full ? '*' : $this->channel_public_columns,
                'name = :name:',
                'bind' => [
                    'name' => mb_strtolower($name),
                ],
            ]);
        }
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_NOT_FOUND);
        }
 
        return $o;
    }
 
    protected function levelExists($level)
    {
        return isset($this->default_levels[mb_strtoupper($level)]);
    }
 
    public function normalizeLevels(array $data)
    {
        foreach ($data as $key => $value) {
            if (!isset($this->default_levels[$key])) {
                unset($data[$key]);
            }
        }
 
        $data = array_merge($this->default_levels, $data);
 
        return $data;
    }
 
    public function checkName($name)
    {
        return preg_match("/^#[A-Za-z0-9áàéèíòóúñç_\.^\\\\\/#%&@=-]{1,29}$/", $name);
    }
 
    protected function checkChannel($name)
    {
        $o = null;
        try {
            $o = $this->get($name, true);
        } catch (\Exception $e) {
        }
        if ($o) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_ALREADY_REGISTERED);
        }
 
        if (!$this->checkName($name)) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_INVALID);
        }
    }
 
    public function countHistory($channel, $nick, $type = 'DEFAULT')
    {
        $manager = $this->getDI()->get('inspircd_nick_manager');
        $manager->checkOperator($nick, 'preoper');
 
        switch ($type) {
            case 'ALL':
                return History::count([
                    [
                        'channel' => mb_strtolower($channel),
                    ],
                ]);
                break;
            case 'ACCESS':
                return History::count([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => new \MongoDB\BSON\Regex('^(REGA|REGS|REGD|REGE|DELA)$'),
                    ],
                ]);
                break;
            case 'AKICKS':
                return History::count([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => new \MongoDB\BSON\Regex('^(AKA|AKS|AKD|AKE)$'),
                    ],
                ]);
                break;
            case 'TOPICS':
                return History::count([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => 'ST',
                    ],
                ]);
                break;
            default:
                return History::count([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => new \MongoDB\BSON\Regex('^(?!(REGA|REGS|REGD|REGE|DELA|AKA|AKS|AKD|AKE|ST))[A-Z]+$'),
                    ],
                ]);
                break;
        }
    }
 
    public function getHistory($channel, $nick, $page = 1, $type = 'DEFAULT')
    {
        $manager = $this->getDI()->get('inspircd_nick_manager');
        $manager->checkOperator($nick, 'preoper');
 
        switch ($type) {
            case 'ALL':
                return History::find([
                    [
                        'channel' => mb_strtolower($channel),
                    ],
                    'sort' => [
                        'date' => -1,
                    ],
                    'limit' => self::PAGE_SIZE,
                    'skip' => self::PAGE_SIZE * ($page - 1),
                ]);
                break;
            case 'ACCESS':
                return History::find([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => new \MongoDB\BSON\Regex('^(REGA|REGS|REGD|REGE|DELA)$'),
                    ],
                    'sort' => [
                        'date' => -1,
                    ],
                    'limit' => self::PAGE_SIZE,
                    'skip' => self::PAGE_SIZE * ($page - 1),
                ]);
                break;
            case 'AKICKS':
                return History::find([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => new \MongoDB\BSON\Regex('^(AKA|AKD|AKE)$'),
                    ],
                    'sort' => [
                        'date' => -1,
                    ],
                    'limit' => self::PAGE_SIZE,
                    'skip' => self::PAGE_SIZE * ($page - 1),
                ]);
                break;
            case 'TOPICS':
                return History::find([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => 'ST',
                    ],
                    'sort' => [
                        'date' => -1,
                    ],
                    'limit' => self::PAGE_SIZE,
                    'skip' => self::PAGE_SIZE * ($page - 1),
                ]);
                break;
            default:
                return History::find([
                    [
                        'channel' => mb_strtolower($channel),
                        'event' => new \MongoDB\BSON\Regex('^(?!(REGA|REGS|REGD|REGE|DELA|AKA|AKD|AKE|ST))[A-Z]+$'),
                    ],
                    'sort' => [
                        'date' => -1,
                    ],
                    'limit' => self::PAGE_SIZE,
                    'skip' => self::PAGE_SIZE * ($page - 1),
                ]);
                break;
        }
    }
 
    public function getHistoryAll($channel)
    {
        return History::find([
            [
                'channel' => mb_strtolower($channel),
            ],
            'sort' => [
                'date' => -1,
            ],
        ]);
    }
 
    public function createHistory($channel, $event, $ip, array $parameters)
    {
        $o = new History();
        $o->date = new \MongoDB\BSON\UTCDateTime(round(microtime(true) * 1000));
        $o->channel = mb_strtolower($channel);
        $o->event = $event;
        $o->ip = $ip;
        $o->parameters = $parameters;
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_CREATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    protected function create(array $data, $ip)
    {
        $this->checkChannel($data['name']);
 
        $defaults = [
            'successor' => new \Phalcon\Db\RawValue("NULL"),
            'section' => new \Phalcon\Db\RawValue("NULL"),
            'url' => new \Phalcon\Db\RawValue("NULL"),
            'email' => new \Phalcon\Db\RawValue("NULL"),
            'levels' => json_encode($this->normalizeLevels([])),
            'mlock' => '+',
            'is_suspended' => 0,
            'suspend_by' => new \Phalcon\Db\RawValue("NULL"),
            'suspend_ts' => new \Phalcon\Db\RawValue("NULL"),
            'suspend_duration' => new \Phalcon\Db\RawValue("NULL"),
            'suspend_reason' => new \Phalcon\Db\RawValue("NULL"),
            'time_registered' => time(),
            'time_last_use' => time(),
            'last_modes' => '',
            'last_topic' => new \Phalcon\Db\RawValue("NULL"),
            'last_topic_by' => new \Phalcon\Db\RawValue("NULL"),
            'last_topic_time' => new \Phalcon\Db\RawValue("NULL"),
        ];
 
        $data = array_merge($defaults, $data);
        $data['founder'] = mb_strtolower($data['founder']);
        $data['password'] = $this->getDI()->get('security')->hash($data['password']);
        $o = new Channel();
        $o->assign($data);
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_CREATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->setStatus($data['name'], Status::STATUS_REGISTERED, $ip, $data['founder']);
 
        return $o;
    }
 
    protected function checkFounder($founder)
    {
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $o = $nick_manager->get($founder, true);
        if ($o->getTimeRegistered() > time() - 7 * 24 * 3600) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_WAIT);
        }
 
        return $o;
    }
 
    public function new(array $data, $oper, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel');
 
        $o = $this->create(array_merge($data, [
            'founder' => $oper,
        ]), $ip);
 
        $this->createHistory($data['name'], History::EVENT_REGISTER, $ip, [
            'who' => $oper,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function registerRequest(array $data, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $this->checkChannel($data['name']);
        $n = $this->checkFounder($data['founder']);
 
        $o = $this->getStatus($data['name']);
        if ($o) {
            switch ($o->status) {
                case Status::STATUS_DENIED:
                    throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_DENIED);
                    break;
                case Status::STATUS_REJECTED:
                    throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_REJECTED);
                    break;
                case Status::STATUS_DROPPED:
                    throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_DROPPED);
                    break;
                case Status::STATUS_REQUESTED:
                    if ($o->nick == mb_strtolower($data['founder'])) {
                        throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_ALREADY_REQUESTED);
                    } else {
                        throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_ALREADY_IN_PROCESS);
                    }
                    break;
                default:
                    break;
            }
        } else {
            $cnt = Status::count([
                [
                    'status' => (int)Status::STATUS_REQUESTED,
                    'nick' => mb_strtolower($data['founder']),
                ],
            ]);
            if ($cnt > 0) {
                throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_WAIT);
            }
        }
 
        $this->setStatus($data['name'], Status::STATUS_REQUESTED, $ip, $data['founder'], '', $data['password'], $data['description'], $n->getEmail());
 
        $this->createHistory($data['name'], History::EVENT_REQUEST, $ip, [
            'who' => $data['founder'],
        ]);
 
        $dbTx->commit();
    }
 
    public function registerAccept($name, $oper, $section, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel', 'canregister');
 
        $status = $this->getStatus($name);
        if (!$status || $status->status != Status::STATUS_REQUESTED) {
            if ($status && ($status->status == Status::STATUS_REJECTED || $status->status == Status::STATUS_DENIED) && $status->founder) {
                try {
                    $nick_manager->checkOperator($oper, 'devel');
                } catch (\Exception $e) {
                    throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_NOT_REQUESTED);
                }
            } else {
                throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_NOT_REQUESTED);
            }
        }
 
        $this->checkChannel($status->channel);
        $f = $this->checkFounder($status->founder);
 
        $o = $this->create([
            'name' => $status->channel,
            'section' => mb_strtoupper($section),
            'founder' => $status->founder,
            'password' => $status->password,
            'description' => $status->description,
        ], $ip);
 
        $this->createHistory($status->channel, History::EVENT_ACCEPT, $ip, [
            'who' => $oper,
        ]);
        $this->createHistory($status->channel, History::EVENT_REGISTER, $ip, [
            'who' => $status->founder,
        ]);
 
        $dbTx->commit();
 
        $o = $this->get($name, true);
        $this->enqueue($o);
 
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $n = $irc_manager->getNick($status->founder);
        if ($n) {
            $msg = $this->getDI()->get('translate')->translate('Channel %name% registration has been approved.', [
                'name' => $status->channel
            ], 'creg');
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $n['uid'], $msg);
        }
 
        $mailer = $this->getDI()->get('mail');
        $mailer->addTo($f->getEmail());
        $mailer->setLanguage('es');
        $mailer->send(
            'Chat Hispano',
            '[ChatHispano] El registro del canal canal '.$status->channel.' ha sido aceptado',
            'creg_accept',
            null,
            [
                'nick' => $f->getNick(),
                'channel' => $status->channel,
                'images' => [
                    'logo' => 'public/assets/backoffice/img/logo-medium.jpg',
                ],
            ]
        );
 
        return $o;
    }
 
    public function registerCancel($name, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $status = $this->getStatus($name);
        if (!$status || $status->status != Status::STATUS_REQUESTED) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_NOT_REQUESTED);
        }
        if (mb_strtolower($nick) != $status->nick) {
            throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
        }
 
        $this->setStatus($status->channel, Status::STATUS_NOT_REGISTERED, $ip, $status->nick);
 
        $dbTx->commit();
 
        $this->createHistory($name, History::EVENT_CANCEL, $ip, [
            'who' => $nick,
        ]);
    }
 
    public function registerDeny($name, $oper, $ip, $reason)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel', 'canregister');
 
        $status = $this->getStatus($name);
        if (!$status || $status->status != Status::STATUS_REQUESTED) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_NOT_REQUESTED);
        }
 
        $this->setStatus($status->channel, Status::STATUS_DENIED, $ip, $status->nick, $reason, $status->password, $status->description, $status->email);
 
        $this->createHistory($name, History::EVENT_DENY, $ip, [
            'who' => $oper,
            'reason' => $reason,
        ]);
 
        $dbTx->commit();
 
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $n = $irc_manager->getNick($status->nick);
        if ($n) {
            $msg = $this->getDI()->get('translate')->translate('Channel %name% registration has been denied:', [
                'name' => $status->channel
            ], 'creg');
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $n['uid'], $msg);
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $n['uid'], $reason);
        }
 
        $f = $this->checkFounder($status->nick);
        $mailer = $this->getDI()->get('mail');
        $mailer->addTo($f->getEmail());
        $mailer->setLanguage('es');
        $mailer->send(
            'Chat Hispano',
            '[ChatHispano] El registro del canal canal '.$status->channel.' ha sido denegado',
            'creg_denied',
            null,
            [
                'nick' => $f->getNick(),
                'channel' => $status->channel,
                'reason' => $reason,
                'images' => [
                    'logo' => 'public/assets/backoffice/img/logo-medium.jpg',
                ],
            ]
        );
    }
 
    public function registerReject($name, $oper, $ip, $reason)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel', 'canregister');
 
        $status = $this->getStatus($name);
        if (!$status || $status->status != Status::STATUS_REQUESTED) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_NOT_REQUESTED);
        }
 
        $this->setStatus($status->channel, Status::STATUS_REJECTED, $ip, $status->nick, $reason, $status->password, $status->description, $status->email);
 
        $this->createHistory($name, History::EVENT_REJECT, $ip, [
            'who' => $oper,
            'reason' => $reason,
        ]);
 
        $dbTx->commit();
 
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $n = $irc_manager->getNick($status->nick);
        if ($n) {
            $msg = $this->getDI()->get('translate')->translate('Channel %name% registration has been rejected:', [
                'name' => $status->channel
            ], 'creg');
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $n['uid'], $msg);
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $n['uid'], $reason);
        }
 
        $f = $this->checkFounder($status->nick);
        $mailer = $this->getDI()->get('mail');
        $mailer->addTo($f->getEmail());
        $mailer->setLanguage('es');
        $mailer->send(
            'Chat Hispano',
            '[ChatHispano] El registro del canal canal '.$status->channel.' ha sido rechazado',
            'creg_reject',
            null,
            [
                'nick' => $f->getNick(),
                'channel' => $status->channel,
                'reason' => $reason,
                'images' => [
                    'logo' => 'public/assets/backoffice/img/logo-medium.jpg',
                ],
            ]
        );
    }
 
    public function registerAllow($name, $oper, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'preoper');
 
        $status = $this->getStatus($name);
        if (!$status || ($status && $status->status != Status::STATUS_DROPPED && $status->status != Status::STATUS_REJECTED && $status->status != Status::STATUS_DENIED)) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_CHANNEL_NOT_DENIED);
        }
        $this->setStatus($status->channel, Status::STATUS_NOT_REGISTERED, $ip, $status->nick);
 
        $this->createHistory($name, History::EVENT_PERMIT, $ip, [
            'who' => $oper,
        ]);
 
        $dbTx->commit();
    }
 
    protected function update(Channel $o, array $data)
    {
        $o->assign($data);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function suspend($name, $oper, $reason, $duration, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $operator = $nick_manager->checkOperator($oper, 'preoper', '');
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_ALREADY_SUSPENDED);
        }
 
        $seconds = new \Khill\Duration\Duration(str_replace('+', '', $duration));
        if (!$operator->isAdmin() && $seconds->toSeconds() > 10 * 24 * 3600) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPEND_TOO_MUCH_TIME);
        }
        if ($seconds->toSeconds() < 3600) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPEND_TOO_SHORT);
        }
        $o = $this->update($o, [
            'is_suspended' => 1,
            'suspend_by' => mb_strtolower($oper),
            'suspend_ts' => time(),
            'suspend_duration' => $seconds->toSeconds(),
            'suspend_reason' => $reason,
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->setStatus($name, Status::STATUS_SUSPENDED, $ip, $oper, $reason);
 
        $this->createHistory($name, History::EVENT_SUSPEND, $ip, [
            'who' => $oper,
            'reason' => $reason,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function unsuspend($name, $oper, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'preoper');
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() != 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_NOT_SUSPENDED);
        }
 
        $o = $this->update($o, [
            'is_suspended' => 0,
            'suspend_by' => new \Phalcon\Db\RawValue("NULL"),
            'suspend_ts' => new \Phalcon\Db\RawValue("NULL"),
            'suspend_duration' => new \Phalcon\Db\RawValue("NULL"),
            'suspend_reason' => new \Phalcon\Db\RawValue("NULL"),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->setStatus($name, Status::STATUS_REGISTERED, $ip, $oper);
 
        $this->createHistory($name, History::EVENT_REHAB, $ip, [
            'who' => $oper,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function accessList($name, $op, $orderby = 'nick ASC')
    {
        $c = $this->get($name, true);
        $oper = false;
        if (false === $this->checkLevel($c, 'ACC-LIST', $op)) {
            try {
                $nick_manager = $this->getDI()->get('inspircd_nick_manager');
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        return Access::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => $orderby,
        ]);
    }
 
    public function accessCount($name, $op)
    {
        $c = $this->get($name, true);
        $oper = false;
        if (false === $this->checkLevel($c, 'ACC-LIST', $op)) {
            try {
                $nick_manager = $this->getDI()->get('inspircd_nick_manager');
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        return Access::count([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
        ]);
    }
 
    public function accessAdd($name, $nick, $level, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
 
        $name = mb_strtolower($name);
        $nick = mb_strtolower($nick);
 
        $c = $this->get($name, true);
        if ($c->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
        $oper = false;
        if (false === $this->checkLevel($c, 'ACC-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        } else {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
            }
        }
 
        if ($level < -1 || $level >= 500 || $level == 0) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_BAD_LEVEL);
        }
 
        if ($oper === false) {
            $oplvl = $this->getLevel($c, $op);
            if ($level >= $oplvl) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = Access::findFirst([
            'channel = :channel: AND nick = :nick:',
            'bind' => [
                'channel' => $name,
                'nick' => $nick,
            ],
        ]);
        if ($o) {
            if ($oper === false) {
                if ($o->getLevel() >= $oplvl) {
                    throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
                }
            }
            if ($level == $o->getLevel()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_NOT_CHANGED, [
                    'nick' => $nick,
                    'level' => $level,
                ]);
            }
 
            $o->assign([
                'level' => $level,
                'set_by' => $op,
                'set_on' => time(),
            ]);
 
            if (false === $o->save()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_UPDATE, [
                    'message' => join('. ', $o->getMessages()),
                ]);
            }
 
            $discourse_manager = $this->getDI()->get('discourse_manager');
            $discourse_manager->updateChannel($c->getName());
 
            $this->createHistory($name, History::EVENT_ACCESS_SET, $ip, [
                $nick,
                $level,
                $op,
            ]);
            $nick_manager->createHistory($op, NickHistory::EVENT_ACCESS_SET, $ip, [
                $name,
                $nick,
                $level,
            ]);
        } else {
            $nick_manager = $this->getDI()->get('inspircd_nick_manager');
            $n = $nick_manager->get($nick, true);
            if ($n->getOptions()['nochaccess']) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_NICK_HAS_NOCHACCESS, [
                    'nick' => $nick,
                ]);
            }
 
            $o = new Access();
            $o->assign([
                'channel' => mb_strtolower($name),
                'nick' => mb_strtolower($nick),
                'level' => $level,
                'set_by' => $op,
                'set_on' => time(),
            ]);
 
            if (false === $o->create()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_CREATE, [
                    'message' => join('. ', $o->getMessages()),
                ]);
            }
 
            $discourse_manager = $this->getDI()->get('discourse_manager');
            $discourse_manager->updateChannel($c->getName());
 
            $this->createHistory($name, History::EVENT_ACCESS_ADD, $ip, [
                $nick,
                $level,
                $op,
            ]);
            $nick_manager->createHistory($op, NickHistory::EVENT_ACCESS_ADD, $ip, [
                $name,
                $nick,
                $level,
            ]);
        }
        $nick_manager->createHistory($nick, NickHistory::EVENT_ACCESS_RECV, $ip, [
            $name,
            $level,
            $op,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function accessDel($name, $nick, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
 
        $name = mb_strtolower($name);
        $nick = mb_strtolower($nick);
 
        $c = $this->get($name, true);
        if ($c->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
        $oper = false;
        if (false === $this->checkLevel($c, 'ACC-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        } else {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
            }
        }
 
        $o = Access::findFirst([
            'channel = :channel: AND nick = :nick:',
            'bind' => [
                'channel' => $name,
                'nick' => $nick,
            ],
        ]);
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_NOT_FOUND);
        } else {
            if ($oper === false) {
                $oplvl = $this->getLevel($c, $op);
                if ($o->getLevel() >= $oplvl) {
                    throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
                }
            }
 
            if (false === $o->delete()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_DELETE, [
                    'message' => join('. ', $o->getMessages()),
                ]);
            }
        }
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($c->getName());
 
        $this->createHistory($name, History::EVENT_ACCESS_DEL, $ip, [
            $nick,
            $op,
        ]);
        $nick_manager->createHistory($op, NickHistory::EVENT_ACCESS_DEL, $ip, [
            $name,
            $nick,
        ]);
        $nick_manager->createHistory($nick, NickHistory::EVENT_ACCESS_TAKEN, $ip, [
            $name,
            $op,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function accessClear($name, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
 
        $name = mb_strtolower($name);
        $c = $this->get($name, true);
        if ($c->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
        $oper = false;
        if (mb_strtolower($op) != $c->getFounder() && mb_strtolower($op) != $c->getSuccessor()) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        } else {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
            }
        }
 
        $list = $c->getAccess();
        foreach ($list as $o) {
            if (false === $o->delete()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_DELETE, [
                    'message' => join('. ', $o->getMessages()),
                ]);
            }
 
            $this->createHistory($name, History::EVENT_ACCESS_DEL, $ip, [
                $o->getNick(),
                $op,
            ]);
            $nick_manager->createHistory($op, NickHistory::EVENT_ACCESS_DEL, $ip, [
                $name,
                $o->getNick(),
            ]);
            $nick_manager->createHistory($o->getNick(), NickHistory::EVENT_ACCESS_TAKEN, $ip, [
                $name,
                $op,
            ]);
        }
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($c->getName());
 
        $dbTx->commit();
 
        return $c;
    }
 
    public function accessHit($name, $nick, $ts = 0)
    {
        $name = mb_strtolower($name);
        $nick = mb_strtolower($nick);
 
        $o = Access::findFirst([
            'channel = :channel: AND nick = :nick:',
            'bind' => [
                'channel' => $name,
                'nick' => $nick,
            ],
        ]);
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_NOT_FOUND);
        } else {
            if ($ts == 0) {
                $o->setLastUse(time());
            } else {
                $o->setLastUse($ts);
            }
            if (false === $o->save()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_UPDATE, [
                    'message' => join(". ", $o->getMessages()),
                ]);
            }
        }
 
        return $o;
    }
 
    protected function checkMlock($mlock, array $parameters)
    {
        $op = 'on';
        $on = false;
        $off = false;
        $modes = [];
        $params = 0;
        $ml_modes = [];
        $ml_params = [];
        for ($i = 0; $i < strlen($mlock); $i++) {
            $m = substr($mlock, $i, 1);
            if ($m == '+') {
                if ($on == true || $off == true) {
                    return [
                        'status' => false,
                        'errmsg' => $this->getDI()->get('translate')->translate('Symbol %symbol% more than once', ['symbol' => '+']),
                    ];
                }
                $modes[] = '+';
                $ml_modes[] = '+';
                $on = true;
                $op = 'on';
            } elseif ($m == '-') {
                if ($off == true) {
                    return [
                        'status' => false,
                        'errmsg' => $this->getDI()->get('translate')->translate('Symbol %symbol more than once', ['symbol' => '-']),
                    ];
                }
                $modes[] = '-';
                $ml_modes[] = '-';
                $off = true;
                $op = 'off';
            } else {
                if (in_array($m, $modes)) {
                    return [
                        'status' => false,
                        'errmsg' => $this->getDI()->get('translate')->translate('Mode %mode% found more than once', ['mode' => $m]),
                    ];
                }
                if (!isset(self::CHANNEL_MODES[$m])) {
                    return [
                        'status' => false,
                        'errmsg' => $this->getDI()->get('translate')->translate('Unknown mode %mode%', ['mode' => $m]),
                    ];
                }
                if ($m != 'r') { // we exclude +r parameter
                    $modes[] = $m;
                    if (self::CHANNEL_MODES[$m]['params'][$op] > 0) {
                        if (!isset($parameters[$params])) {
                            return [
                                'status' => false,
                                'errmsg' => $this->getDI()->get('translate')->translate('Missing parameter for mode %mode%', ['mode' => $m]),
                            ];
                        }
                        if (isset(self::CHANNEL_MODES[$m]['params']['pattern']) && !preg_match(self::CHANNEL_MODES[$m]['params']['pattern'], $parameters[$params])) {
                            return [
                                'status' => false,
                                'errmsg' => $this->getDI()->get('translate')->translate('Wrong parameter %param% for mode %mode%', [
                                    'mode' => $m,
                                    'param' => $parameters[$params]
                                ]),
                            ];
                        }
                    }
                    $ml_modes[] = $m;
                    if ($m != 'k') {
                        if (self::CHANNEL_MODES[$m]['params'][$op] > 0) {
                            $ml_params[] = $parameters[$params];
                        }
                    }
                    $params += self::CHANNEL_MODES[$m]['params'][$op];
                }
            }
        }
        if (count($ml_params) > 0) {
            $mlock = join('', $ml_modes).' '.join(' ', $ml_params);
        } else {
            $mlock = join('', $ml_modes);
        }
        if (count($parameters) > 0) {
            $mode = join('', $modes).' '.join(' ', $parameters);
        } else {
            $mode = join('', $modes);
        }
 
        return [
            'status' => true,
            'mlock' => $mlock,
            'mode' => $mode,
        ];
    }
 
    public function setMlock($name, $mlock, array $parameters, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $c = $this->get($name, true);
        if ($c->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
        $oper = false;
        if (false === $this->checkLevel($c, 'SET', $nick)) {
            try {
                $nick_manager = $this->getDI()->get('inspircd_nick_manager');
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $result = $this->checkMlock($mlock, $parameters);
        if ($result['status'] === false) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_MLOCK_BAD_FORMAT, [
                'errmsg' => $result['errmsg'],
            ]);
        }
 
        $o = $this->update($c, [
            'mlock' => $result['mlock'],
        ]);
 
        $this->createHistory($name, History::EVENT_SET_MLOCK, $ip, [
            'mlock' => $result['mlock'],
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return [
            'mlock' => $result['mlock'],
            'mode' => $result['mode'],
            'obj' => $o,
        ];
    }
 
    public function setPassword($name, $password, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'password' => $this->getDI()->get('security')->hash($password),
        ]);
 
        $this->unidentifyChannel($name);
 
        $this->createHistory($name, History::EVENT_SET_PASSWORD, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function checkPassword($name, $password)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        if (!$this->getDI()->get('security')->checkHash($password, $o->getPassword())) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_BAD_PASSWORD);
        }
 
        return $o;
    }
 
    public function setFounder($name, $nick, $founder, $ip, $checkCredentials = true)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        if ($checkCredentials === true) {
            $oper = false;
            if (false === $this->checkLevel($o, 500, $nick)) {
                try {
                    $nick_manager->checkOperator($nick, 'preoper');
                    $oper = true;
                } catch (\Exception $e) {
                    throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
                }
            }
        }
        $f = $nick_manager->get($founder, true);
 
        if ($f->getOptions()['nochaccess']) {
            $a = Access::findFirst([
                'channel = :channel: AND nick = :nick:',
                'bind' => [
                    'channel' => mb_strtolower($name),
                    'nick' => mb_strtolower($founder),
                ],
            ]);
            if (!$a) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_SET_FOUNDER_NICK_HAS_NOCHACCESS, [
                    'nick' => $founder,
                ]);
            }
        }
 
        if (mb_strtolower($founder) == $o->getSuccessor()) {
            $o = $this->update($o, [
                'founder' => mb_strtolower($founder),
                'successor' => new \Phalcon\Db\RawValue("NULL"),
            ]);
        } else {
            $o = $this->update($o, [
                'founder' => mb_strtolower($founder),
            ]);
        }
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->unidentifyChannel($name);
 
        $this->createHistory($name, History::EVENT_SET_FOUNDER, $ip, [
            'founder' => $founder,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setSuccessor($name, $nick, $successor, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
        $s = $nick_manager->get($successor, true);
 
        if ($s->getOptions()['nochaccess']) {
            $a = Access::findFirst([
                'channel = :channel: AND nick = :nick:',
                'bind' => [
                    'channel' => mb_strtolower($name),
                    'nick' => mb_strtolower($successor),
                ],
            ]);
            if (!$a) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_SET_SUCCESSOR_NICK_HAS_NOCHACCESS, [
                    'nick' => $successor,
                ]);
            }
        }
 
        $o = $this->update($o, [
            'successor' => mb_strtolower($successor),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->createHistory($name, History::EVENT_SET_SUCCESSOR, $ip, [
            'successor' => $successor,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function unsetSuccessor($name, $nick, $ip, $checkCredentials = true)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        if ($checkCredentials === true) {
            $nick_manager = $this->getDI()->get('inspircd_nick_manager');
            $oper = false;
            if (false === $this->checkLevel($o, 500, $nick)) {
                try {
                    $nick_manager->checkOperator($nick, 'preoper');
                    $oper = true;
                } catch (\Exception $e) {
                    throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
                }
            }
        }
 
        if (strlen($o->getSuccessor()) <= 0) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_NO_SUCCESSOR);
        }
 
        $o = $this->update($o, [
            'successor' => new \Phalcon\Db\RawValue("NULL"),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->createHistory($name, History::EVENT_UNSET_SUCCESSOR, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setSection($name, $nick, $section, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        try {
            $nick_manager->checkOperator($nick, 'devel', 'canregister');
        } catch (\Exception $e) {
            throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
        }
 
        $section = mb_strtoupper($section);
        $s = $this->getSection($section);
 
        $o = $this->update($o, [
            'section' => $section,
        ]);
 
        $this->createHistory($name, History::EVENT_SET_SECTION, $ip, [
            'section' => $section,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setDescription($name, $nick, $description, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'devel', 'canregister');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'description' => $description,
        ]);
 
        $this->createHistory($name, History::EVENT_SET_DESCRIPTION, $ip, [
            'description' => $description,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setGi($name, $nick, $value, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($nick, 'devel');
 
        $o = $this->update($o, [
            'ig' => mb_strtolower($value) == 'on' ? 1 : 0,
        ]);
 
        $event = $o->getIg() == 1 ? History::EVENT_SET_GENERAL_INTEREST : History::EVENT_UNSET_GENERAL_INTEREST;
        $this->createHistory($name, $event, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setUrl($name, $nick, $url, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'url' => $url,
        ]);
 
        $this->createHistory($name, History::EVENT_SET_URL, $ip, [
            'url' => $url,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function unsetUrl($name, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'url' => new \Phalcon\Db\RawValue("NULL"),
        ]);
 
        $this->createHistory($name, History::EVENT_UNSET_URL, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setEmail($name, $nick, $email, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'email' => $email,
        ]);
 
        $this->createHistory($name, History::EVENT_SET_EMAIL, $ip, [
            'email' => $email,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function unsetEmail($name, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'email' => new \Phalcon\Db\RawValue("NULL"),
        ]);
 
        $this->createHistory($name, History::EVENT_UNSET_EMAIL, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setEntrymsg($name, $nick, $entrymsg, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'entrymsg' => $entrymsg,
        ]);
 
        $this->createHistory($name, History::EVENT_SET_ENTRYMSG, $ip, [
            'entrymsg' => $entrymsg,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function unsetEntrymsg($name, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'entrymsg' => new \Phalcon\Db\RawValue("NULL"),
        ]);
 
        $this->createHistory($name, History::EVENT_UNSET_ENTRYMSG, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setOption($name, $nick, $option, $value, $ip, $check_operator = true)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        try {
            $o = $this->get($name, true);
            if ($o->getIsSuspended() == 1) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
            }
 
            $nick_manager = $this->getDI()->get('inspircd_nick_manager');
            $oper = false;
            if (false === $this->checkLevel($o, $option == 'openai' ? 'SET' : 500, $nick)) {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } else {
                try {
                    $nick_manager->checkOperator($nick, 'preoper');
                    $oper = true;
                } catch (\Exception $e) {
                }
            }
            if ($check_operator === true && in_array($option, ['noakick', 'noexpire', 'noads', 'expirewarn', 'forum', 'underage'])) {
                if ($oper === true) {
                    if (in_array($option, ['noexpire', 'noads', 'expirewarn', 'forum', 'underage'])) {
                        $nick_manager->checkOperator($nick, 'devel');
                    }
                } else {
                    throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
                }
            }
 
            $options = $o->getOptions();
            $v = mb_strtolower($value) == 'on' ? true : false;
            if (isset($options[$option]) && $v === $options[$option]) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_OPTION_NOT_CHANGED);
            }
            // We only allow to activate openai option if channel is IG
            if ($option == 'openai' && $v === true && $o->getIg() != 1) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
 
            $options[$option] = $v;
            $o = $this->update($o, [
                'options' => $options,
            ]);
 
            $this->createHistory($name, $options[$option] ? History::EVENT_SET_OPTION : History::EVENT_UNSET_OPTION, $ip, [
                'option' => $option,
                'who' => $nick,
            ]);
 
            $dbTx->commit();
        } catch (\Exception $e) {
            $dbTx->rollback();
            throw new \Exception($e->getMessage(), $e->getCode());
        }
 
        return $o;
    }
 
    public function setLastModes($name, $modes)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
        $o = $this->update($o, [
            'last_modes' => $modes,
        ]);
 
        return $o;
    }
 
    public function setTopic($name, $nick, $topic, $ts, $ip, $check_credentials = true)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        if (true === $check_credentials) {
            $nick_manager = $this->getDI()->get('inspircd_nick_manager');
            $oper = false;
            if (false === $this->checkLevel($o, 'TOPIC', $nick)) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'last_topic' => $topic,
            'last_topic_by' => mb_strtolower($nick),
            'last_topic_time' => $ts,
        ]);
 
        $this->createHistory($name, History::EVENT_SET_TOPIC, $ip, [
            'topic' => $topic,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function setLevel($name, $nick, $level, $value, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        if (!$this->levelExists($level)) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_LEVEL_DOES_NOT_EXIST);
        }
        if (!is_numeric($value) || $value < -1 || $value > 500) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_LEVEL_OUT_OF_BOUNDS);
        }
 
        $levels = $this->normalizeLevels(json_decode($o->getLevels(), true));
        $levels[strtoupper($level)] = $value;
        $levels = $this->normalizeLevels($levels);
        $o = $this->update($o, [
            'levels' => json_encode($levels),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->createHistory($name, History::EVENT_SET_LEVEL, $ip, [
            'level' => strtoupper($level),
            'value' => $value,
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function disableLevel($name, $nick, $level, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        if (!$this->levelExists($level)) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_LEVEL_DOES_NOT_EXIST);
        }
 
        $levels = $this->normalizeLevels(json_decode($o->getLevels(), true));
        $levels[strtoupper($level)] = 'DIS';
        $levels = $this->normalizeLevels($levels);
        $o = $this->update($o, [
            'levels' => json_encode($levels),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->createHistory($name, History::EVENT_DISABLE_LEVEL, $ip, [
            'level' => strtoupper($level),
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function resetLevels($name, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 500, $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = $this->update($o, [
            'levels' => json_encode($this->default_levels),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->createHistory($name, History::EVENT_RESET_LEVELS, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function updateLastUse($channel)
    {
        $o = $this->get($channel, true);
 
        $o = $this->update($o, [
            'time_last_use' => time(),
        ]);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        try {
            $o = $this->setOption($o->getName(), 'ChatHispano', 'expirewarn', 'off', '127.0.0.1', false);
        } catch (\Exception $e) {
        }
 
        return $o;
    }
 
    protected function checkAkickMask($mask, $exception = false, $oper = false)
    {
        $restrictedPrefixes = [
            "U:",
            "G:",
            "X:",
        ];
        if (false === $oper && in_array(substr($mask, 0, 2), $restrictedPrefixes)) {
            throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
        }
        // only nick, we add !*@* to the end
        if (preg_match("/^[^\s:!@]+$/", $mask)) {
            $mask .= '!*@*';
        }
        $patterns = [
            "/^r:[^\\s]+$/", // realname
            "/^a:[^\\s!@\\+]+![^\\s!@\\+]+@[^\\s!@\\+]+\\+[^\\s]+$/", // mask+realname
            "/^G:([A-Z]{2}|\\*)!([A-Z]{2,3}|\\*)!([A-Za-z0-9\\*]+|\\*)!([A-Za-z0-9\\*]+|\\*)$/", // geolocation
            "/^G:([A-Z]{2}|\\*)!([A-Z]{2}|\\*)!([A-Z]{2,3}|\\*)!([A-Za-z0-9\\*]+|\\*)!([A-Za-z0-9\\*]+|\\*)$/", // geolocation with continent
            "/^g:([a-z]:)?[^\\s!@\\+]+![^\\s!@\\+]+@[^\\s!@\\+]+\\+([A-Z]{2}|\\*)!([A-Z]{2}|\\*)!([A-Z]{2,3}|\\*)!([A-Za-z0-9\\*]+|\\*)!([A-Za-z0-9\\*]+|\\*)$/", // mask + geolocation
            "/^U:[a-z0-9]{16}$/", // unique user id (android)
            "/^U:[a-z0-9]{32}$/", // unique user id (webchat)
            "/^X:[a-z0-9]{16}!([A-Z]{2}|\\*)!([A-Z]{2}|\\*)!([A-Z]{2,3}|\\*)!([A-Za-z0-9\\*]+|\\*)!([A-Za-z0-9\\*]+|\\*)$/", // unique user id (android) + geolocation
            "/^X:[a-z0-9]{32}!([A-Z]{2}|\\*)!([A-Z]{2}|\\*)!([A-Z]{2,3}|\\*)!([A-Za-z0-9\\*]+|\\*)!([A-Za-z0-9\\*]+|\\*)$/", // unique user id (webchat) + geolocation
            "/^([a-z]:)?[^\\s!@\\+]+![^\\s!@\\+]+@[^\\s!@\\+]+$/", // rest of
        ];
        $valid = false;
        foreach ($patterns as $p) {
            if (preg_match($p, $mask)) {
                $valid = true;
                break;
            }
        }
        if ($valid === false) {
            throw new Exception(null, $exception ? ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_CREATE : ErrorCodes::ERR_CHANNEL_AKICK_CREATE, [
                'message' => 'Invalid mask',
            ]);
        }
        // Convert to lowercase nick part in regular masks + a:, g: extbans
        if (substr($mask, 1, 1) != ':' || substr($mask, 0, 2) == 'a:' || substr($mask, 0, 2) == 'g:') {
            $parts = explode('!', $mask);
            $mask = strtolower($parts[0]).'!'.join('!', array_slice($parts, 1));
        }
 
        return $mask;
    }
 
    public function akickAdd($channel, $mask, $reason, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($channel, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        } else {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
            }
        }
 
        if ($oper === false) {
            $redis = $this->getDI()->get('redis_cluster');
            $v = $redis->get('chathispano_inspircd_akick_delete_'.str_replace('#', '', $channel).'_'.$mask);
            if ($v && $v == 1) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_WAIT);
            }
        }
 
        $mask = $this->checkAkickMask($mask, false, $oper);
 
        $o = new Akick();
        $o->assign([
            'channel' => mb_strtolower($channel),
            'mask' => $mask,
            'reason' => $reason,
            'author' => mb_strtolower($op),
            'created_on' => new \Phalcon\Db\RawValue("NOW()"),
            'last_hit' => new \Phalcon\Db\RawValue("NULL"),
            'hits' => 0,
        ]);
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_CREATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->createHistory(mb_strtolower($channel), History::EVENT_AKICK_ADD, $ip, [
            $mask,
            mb_strtolower($op),
            $reason,
        ]);
        $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_ADD, $ip, [
            mb_strtolower($channel),
            $mask,
            $reason,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickSet($channel, $mask, $reason, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($channel, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = Akick::findFirst([
            'channel = :channel: AND mask = :mask:',
            'bind' => [
                'channel' => mb_strtolower($channel),
                'mask' => $mask,
            ],
        ]);
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_NOT_FOUND);
        }
 
        $o->assign([
            'reason' => $reason,
            'author' => mb_strtolower($op),
        ]);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->createHistory(mb_strtolower($channel), History::EVENT_AKICK_SET, $ip, [
            $mask,
            mb_strtolower($op),
            $reason,
        ]);
        $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_SET, $ip, [
            mb_strtolower($channel),
            $mask,
            $reason,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickDel($name, $mask, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = Akick::findFirst([
            'channel = :channel: AND mask = :mask:',
            'bind' => [
                'channel' => mb_strtolower($name),
                'mask' => $mask,
            ],
        ]);
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_NOT_FOUND);
        }
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_DELETE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $redis = $this->getDI()->get('redis_cluster');
        $redis->set('chathispano_inspircd_akick_delete_'.str_replace('#', '', $name).'_'.$mask, 1, 3600 * 24);
 
        $this->createHistory(mb_strtolower($name), History::EVENT_AKICK_DEL, $ip, [
            $mask,
            mb_strtolower($op),
        ]);
        $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_DEL, $ip, [
            mb_strtolower($name),
            $mask,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickClear($name, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper', '', true);
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $list = $o->getAkick();
        $masks = [];
        foreach ($list as $a) {
            $masks[] = $a->getMask();
            if (false === $a->delete()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_DELETE, [
                    'message' => join('. ', $a->getMessages()),
                ]);
            }
        }
 
        foreach ($masks as $mask) {
            $redis = $this->getDI()->get('redis_cluster');
            $redis->set('chathispano_inspircd_akick_delete_'.str_replace('#', '', $name).'_'.$mask, 1, 3600 * 24);
 
            $this->createHistory(mb_strtolower($name), History::EVENT_AKICK_DEL, $ip, [
                $mask,
                mb_strtolower($op),
            ]);
            $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_DEL, $ip, [
                mb_strtolower($name),
                $mask,
            ]);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickList($name, $op, $ip, $order = 'mask ASC')
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-LIST', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        return Akick::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => $order,
        ]);
    }
 
    public function akickEnforce($name, $users, $op, $ip)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-LIST', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $exceptions = AkickException::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => 'hits DESC',
        ]);
 
        $list = Akick::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => 'hits DESC',
        ]);
 
        $result = [];
        foreach ($users as $u) {
            $exception = false;
            foreach ($exceptions as $o) {
                if ($nick_manager->checkMask($u, $o->getMask())) {
                    $exception = true;
                    break;
                }
            }
            if (false === $exception) {
                foreach ($list as $o) {
                    if (isset($u['modes']) && preg_match("/[Bok]/", $u['modes'])) {
                        continue;
                    }
                    if ($nick_manager->checkMask($u, $o->getMask())) {
                        $result[] = array_merge($u, [
                            'mask' => $o->getMask(),
                            'reason' => $o->getReason(),
                            'author' => $o->getAuthor(),
                        ]);
                        break;
                    }
                }
            }
        }
 
        return $result;
    }
 
    public function akickAffectsUser(Channel $c, array $user)
    {
        if ($c->getIsSuspended() == 1) {
            return false;
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
 
        $list = AkickException::find([
            'channel = :channel:',
            'bind' => [
                'channel' => $c->getName(),
            ],
            'order' => 'hits DESC',
        ]);
        foreach ($list as $o) {
            if ($nick_manager->checkMask($user, $o->getMask())) {
                return false;
            }
        }
 
        $list = Akick::find([
            'channel = :channel:',
            'bind' => [
                'channel' => $c->getName(),
            ],
            'order' => 'hits DESC',
        ]);
        foreach ($list as $o) {
            if ($nick_manager->checkMask($user, $o->getMask())) {
                return true;
            }
        }
 
        return false;
    }
 
    public function akickCheck($name, $user, $op, $ip)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (mb_strtolower($user['nick']) != mb_strtolower($op)) {
            if (false === $this->checkLevel($o, 'AKICK-LIST', $op)) {
                try {
                    $nick_manager->checkOperator($op, 'preoper');
                    $oper = true;
                } catch (\Exception $e) {
                    throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
                }
            }
        }
 
        $list = AkickException::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => 'hits DESC',
        ]);
        foreach ($list as $o) {
            if ($nick_manager->checkMask($user, $o->getMask())) {
                return [];
            }
        }
 
        $list = Akick::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => 'hits DESC',
        ]);
 
        $result = [];
        foreach ($list as $o) {
            if ($nick_manager->checkMask($user, $o->getMask())) {
                $result[] = [
                    'mask' => $o->getMask(),
                    'reason' => $o->getReason(),
                    'author' => $o->getAuthor(),
                ];
                break;
            }
        }
 
        return $result;
    }
 
    public function akickCount($name, $op)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-LIST', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        return Akick::count([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
        ]);
    }
 
    public function akickHitCount($channel, $mask, $victim)
    {
        $parameters = [];
        if ($channel != '') {
            $parameters['channel'] = mb_strtolower($channel);
        }
        if ($mask != '') {
            $parameters['mask'] = mb_strtolower($mask);
        }
        if ($victim != '') {
            $parameters['victim'] = mb_strtolower($victim);
        }
 
        return Hit::count([
            $parameters,
        ]);
    }
 
    public function akickHitList($channel, $mask, $victim, $page = 1)
    {
        $parameters = [];
        if ($channel != '') {
            $parameters['channel'] = mb_strtolower($channel);
        }
        if ($mask != '') {
            $parameters['mask'] = mb_strtolower($mask);
        }
        if ($victim != '') {
            $parameters['victim'] = mb_strtolower($victim);
        }
 
        return Hit::find([
            $parameters,
            'limit' => self::PAGE_SIZE,
            'skip' => ($page - 1) * self::PAGE_SIZE,
        ]);
    }
 
    public function akickHit($name, $mask, $nick, $ts = 0)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = Akick::findFirst([
            'channel = :name: AND mask = :mask:',
            'bind' => [
                'name' => mb_strtolower($name),
                'mask' => $mask,
            ],
        ]);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_NOT_FOUND);
        }
 
        if ($ts == 0) {
            $o->setLastHit(new \Phalcon\Db\RawValue("NOW()"));
        } else {
            $o->setLastHit(date('Y-m-d H:i:s', $ts));
        }
        $o->setHits($o->getHits() + 1);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_UPDATE, [
                'message' => join(". ", $o->getMessages()),
            ]);
        }
 
        $h = new Hit();
        $h->date = new \MongoDB\BSON\UTCDateTime(time() * 1000);
        $h->channel = $o->getChannel();
        $h->mask = $mask;
        $h->victim = mb_strtolower($nick);
        $h->reason = $o->getReason();
        if (false === $h->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_UPDATE, [
                'message' => join(". ", $h->getMessages()),
            ]);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickFind()
    {
        return Akick::find();
    }
 
    public function akickExceptionAdd($channel, $mask, $reason, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($channel, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        } else {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
            }
        }
 
        $mask = $this->checkAkickMask($mask, true, $oper);
 
        $o = new AkickException();
        $o->assign([
            'channel' => mb_strtolower($channel),
            'mask' => $mask,
            'reason' => $reason,
            'author' => mb_strtolower($op),
            'created_on' => new \Phalcon\Db\RawValue("NOW()"),
            'last_hit' => new \Phalcon\Db\RawValue("NULL"),
            'hits' => 0,
        ]);
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_CREATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->createHistory(mb_strtolower($channel), History::EVENT_AKICK_EXCEPTION_ADD, $ip, [
            $mask,
            mb_strtolower($op),
            $reason,
        ]);
        $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_EXCEPTION_ADD, $ip, [
            mb_strtolower($channel),
            $mask,
            $reason,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickExceptionSet($channel, $mask, $reason, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($channel, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = AkickException::findFirst([
            'channel = :channel: AND mask = :mask:',
            'bind' => [
                'channel' => mb_strtolower($channel),
                'mask' => $mask,
            ],
        ]);
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_NOT_FOUND);
        }
 
        $o->assign([
            'reason' => $reason,
            'author' => mb_strtolower($op),
        ]);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->createHistory(mb_strtolower($channel), History::EVENT_AKICK_EXCEPTION_SET, $ip, [
            $mask,
            mb_strtolower($op),
            $reason,
        ]);
        $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_EXCEPTION_SET, $ip, [
            mb_strtolower($channel),
            $mask,
            $reason,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickExceptionDel($name, $mask, $op, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-CHANGE', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        $o = AkickException::findFirst([
            'channel = :channel: AND mask = :mask:',
            'bind' => [
                'channel' => mb_strtolower($name),
                'mask' => $mask,
            ],
        ]);
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_NOT_FOUND);
        }
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_DELETE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->createHistory(mb_strtolower($name), History::EVENT_AKICK_EXCEPTION_DEL, $ip, [
            $mask,
            mb_strtolower($op),
        ]);
        $nick_manager->createHistory(mb_strtolower($op), NickHistory::EVENT_AKICK_EXCEPTION_DEL, $ip, [
            mb_strtolower($name),
            $mask,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickExceptionList($name, $op, $ip, $order = 'mask ASC')
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-LIST', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        return AkickException::find([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
            'order' => $order,
        ]);
    }
 
    public function akickExceptionCount($name, $op)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $oper = false;
        if (false === $this->checkLevel($o, 'AKICK-LIST', $op)) {
            try {
                $nick_manager->checkOperator($op, 'preoper');
                $oper = true;
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        return AkickException::count([
            'channel = :channel:',
            'bind' => [
                'channel' => mb_strtolower($name),
            ],
        ]);
    }
 
    public function akickExceptionHitCount($channel, $mask, $victim)
    {
        $parameters = [];
        if ($channel != '') {
            $parameters['channel'] = mb_strtolower($channel);
        }
        if ($mask != '') {
            $parameters['mask'] = mb_strtolower($mask);
        }
        if ($victim != '') {
            $parameters['victim'] = mb_strtolower($victim);
        }
 
        return ExceptionHit::count([
            $parameters,
        ]);
    }
 
    public function akickExceptionHitList($channel, $mask, $victim, $page = 1)
    {
        $parameters = [];
        if ($channel != '') {
            $parameters['channel'] = mb_strtolower($channel);
        }
        if ($mask != '') {
            $parameters['mask'] = mb_strtolower($mask);
        }
        if ($victim != '') {
            $parameters['victim'] = mb_strtolower($victim);
        }
 
        return ExceptionHit::find([
            $parameters,
            'limit' => self::PAGE_SIZE,
            'skip' => ($page - 1) * self::PAGE_SIZE,
        ]);
    }
 
    public function akickExceptionHit($name, $mask, $nick, $ts = 0)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = AkickException::findFirst([
            'channel = :name: AND mask = :mask:',
            'bind' => [
                'name' => mb_strtolower($name),
                'mask' => $mask,
            ],
        ]);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_NOT_FOUND);
        }
 
        if ($ts == 0) {
            $o->setLastHit(new \Phalcon\Db\RawValue("NOW()"));
        } else {
            $o->setLastHit(date('Y-m-d H:i:s', $ts));
        }
        $o->setHits($o->getHits() + 1);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_UPDATE, [
                'message' => join(". ", $o->getMessages()),
            ]);
        }
 
        $h = new Hit();
        $h->date = new \MongoDB\BSON\UTCDateTime(time() * 1000);
        $h->channel = $o->getChannel();
        $h->mask = $mask;
        $h->victim = mb_strtolower($nick);
        $h->reason = $o->getReason();
        if (false === $h->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_AKICK_EXCEPTION_UPDATE, [
                'message' => join(". ", $h->getMessages()),
            ]);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function akickExceptionFind()
    {
        return AkickException::find();
    }
 
    public function drop($name, $oper, $reason, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'preoper', '');
 
        $o = $this->get($name, true);
 
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DELETE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $this->enqueueDelete($o);
 
        $this->setStatus($name, Status::STATUS_DROPPED, $ip, $oper, $reason);
 
        $this->createHistory($name, History::EVENT_DROPPED, $ip, [
            'who' => $oper,
            'reason' => $reason,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function delSuccessor($name, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        if (strlen($o->getSuccessor()) <= 0) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_NO_SUCCESSOR);
        }
 
        if (mb_strtolower($o->getSuccessor()) != mb_strtolower($nick)) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUCCESSOR_MISMATCH);
        }
 
        $o = $this->update($o, [
            'successor' => new \Phalcon\Db\RawValue("NULL"),
        ]);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($o->getName());
 
        $this->createHistory($name, History::EVENT_UNSET_SUCCESSOR, $ip, [
            'who' => $nick,
        ]);
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function listForbidAll($cached = false)
    {
        if ($cached === true) {
            return Forbid::find([
                'cache' => [
                    'key' => 'channel-forbids-all',
                    'lifetime' => 3600,
                ],
            ]);
        } else {
            return Forbid::find();
        }
    }
 
    public function countForbid()
    {
        return Forbid::count();
    }
 
    public function getChannelsByPattern($pattern)
    {
        $matched = [];
        $list = $this->listAll();
        foreach ($list as $o) {
            $name = str_replace('#', '', $o->getName());
            if (preg_match('#'.$pattern.'#i', $name)) {
                $matched[] = $o;
            }
        }
 
        return $matched;
    }
 
    public function listForbid($page = 1)
    {
        return Forbid::find([
            'limit' => self::PAGE_SIZE,
            'offset' => ($page - 1) * self::PAGE_SIZE,
        ]);
    }
 
    public function getForbid($id)
    {
        $o = Forbid::findFirst([
            'id = :id:',
            'bind' => [
                'id' => $id,
            ],
        ]);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_FORBID_NOT_FOUND);
        }
 
        return $o;
    }
 
    public function checkForbid($name, $cached = false)
    {
        $list = $this->listForbidAll($cached);
 
        foreach ($list as $o) {
            if (preg_match("#".$o->getPattern()."#i", str_replace('#', '', $name))) {
                return true;
            }
        }
 
        return false;
    }
 
    public function checkForbiddenRegisteredChannels(Forbid $f, $oper, $force, $ip)
    {
        $list = Channel::find();
        foreach ($list as $o) {
            if (preg_match("#".$f->getPattern()."#i", str_replace('#', '', $o->getName()))) {
                if ($force === true) {
                    /**
                     * We have to drop the nick as it is not allowed to use it anymore
                     */
                    $this->drop($o->getName(), $oper, 'El canal ya no puede usarse (FORBID)', $ip);
                } else {
                    /**
                     * We drop an exception as we warn that pattern will drop registered nicks
                     * and it is not forced
                     */
                    throw new Exception(null, ErrorCodes::ERR_CHANNEL_FORBID_REGISTERED_AFFECTED);
                }
            }
        }
    }
 
    public function newForbid(array $data, $oper, $force, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel');
 
        $date = date('Y-m-d H:i:s');
        $defaults = [
            'created_by' => $oper,
            'created_at' => $date,
            'updated_by' => $oper,
            'updated_at' => $date,
        ];
        $data = array_merge($defaults, $data);
        $o = new Forbid();
        $o->assign($data);
 
        $this->checkForbiddenRegisteredChannels($o, $oper, $force, $ip);
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_FORBID_CREATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function updateForbid($id, array $data, $oper, $force, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel');
 
        $o = $this->getForbid($id);
        $defaults = [
            'updated_by' => $oper,
            'updated_at' => date('Y-m-d H:i:s'),
        ];
        $data = array_merge($defaults, $data);
        $o->assign($data);
 
        $this->checkForbiddenRegisteredChannels($o, $oper, $force, $ip);
 
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_FORBID_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function deleteForbid($id, $oper)
    {
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->checkOperator($oper, 'devel');
 
        $o = $this->getForbid($id);
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_FORBID_DELETE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function getChannelAccess($channel, $nick)
    {
        $o = Access::findFirst([
            'channel = :channel: AND nick = :nick:',
            'bind' => [
                'channel' => mb_strtolower($channel),
                'nick' => mb_strtolower($nick),
            ],
        ]);
 
        return $o;
    }
 
    public function getLevel(/*Channel|string*/ $channel, $nick)
    {
        $nick = strtolower($nick);
        if (is_object($channel)) {
            $c = $channel;
        } else {
            $c = $this->get($channel, true);
        }
 
        // Channel owner
        if (mb_strtolower($c->getFounder()) == $nick) {
            return 500;
        }
 
        // Identified
        $redis = $this->getDI()->get('redis_cluster');
        $json = $redis->hGet(self::REDIS_HASH_CHANNEL_IDENTIFY, $c->getName());
        if ($json) {
            $identified = json_decode($json, true);
        } else {
            $identified = [];
        }
        if (in_array($nick, $identified)) {
            return 500;
        }
 
        // Sucessor
        if (mb_strtolower($c->getSuccessor()) == $nick) {
            return self::SUCCESSOR_LEVEL;
        }
 
        // Register
        $a = $this->getChannelAccess($c->getName(), $nick);
        if ($a) {
            return $a->getLevel();
        }
 
        return 0;
    }
 
    public function checkLevel(/*Channel|string*/ $channel, $level, $nick)
    {
        $nick = strtolower($nick);
        if (is_object($channel)) {
            $c = $channel;
        } else {
            $c = $this->get($channel, true);
        }
 
        $userlevel = 0;
        $is_founder = false;
        if (mb_strtolower($c->getFounder()) == $nick) {
            $userlevel = 500;
            $is_founder = true;
        } else {
            $redis = $this->getDI()->get('redis_cluster');
            $json = $redis->hGet(self::REDIS_HASH_CHANNEL_IDENTIFY, $c->getName());
            if ($json) {
                $identified = json_decode($json, true);
            } else {
                $identified = [];
            }
            if (in_array($nick, $identified)) {
                $userlevel = 500;
                $is_founder = true;
            } elseif ($c->getSuccessor() && mb_strtolower($c->getSuccessor()) == $nick) {
                $userlevel = self::SUCCESSOR_LEVEL;
            } else {
                $a = $this->getChannelAccess($c->getName(), $nick);
                if ($a) {
                    $userlevel = $a->getLevel();
                }
            }
        }
 
        if (!is_numeric($level)) {
            $levels = $this->normalizeLevels(json_decode($c->getLevels(), true));
            if ($levels[$level] === 'DIS' || $levels[$level] === 'DISABLED') {
                if ($level === 'AUTOOP' && $is_founder === true) {
                    return true;
                }
                return false;
            }
            if ($level === 'NOJOIN' || $level === 'AUTODEOP' || $level === 'AUTODEVOICE') {
                return $userlevel <= $levels[$level];
            }
            if ($levels[$level] === 0 && ($level === 'AUTOOP' || $level === 'AUTOVOICE')) {
                return false;
            }
            return $userlevel >= $levels[$level];
        }
        return $userlevel >= $level;
    }
 
    public function checkInvite($channel, $nick)
    {
        return $this->checkLevel($channel, 'INVITE', $nick);
    }
 
    public function checkVoice($channel, $nick)
    {
        return $this->checkLevel($channel, 'VOICEDEVOICE', $nick);
    }
 
    public function checkDevoice($channel, $nick)
    {
        return $this->checkLevel($channel, 'VOICEDEVOICE', $nick);
    }
 
    public function checkOp($channel, $nick)
    {
        return $this->checkLevel($channel, 'OPDEOP', $nick);
    }
 
    public function checkDeop($channel, $nick)
    {
        return $this->checkLevel($channel, 'OPDEOP', $nick);
    }
 
    public function checkUnban($channel, $nick)
    {
        return $this->checkLevel($channel, 'UNBAN', $nick);
    }
 
    public function checkTopic($channel, $nick)
    {
        return $this->checkLevel($channel, 'TOPIC', $nick);
    }
 
    public function checkBlogger($channel, $nick)
    {
        return $this->checkLevel($channel, 'BLOGGER', $nick);
    }
 
    public function checkForum($channel, $nick)
    {
        return $this->checkLevel($channel, 'FORUM', $nick);
    }
 
    public function delaccess($channel, $nick, $ip)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        $nick = mb_strtolower($nick);
        $c = $this->get($channel, true);
        if ($c->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $a = $this->getChannelAccess($channel, $nick);
        if ($a) {
            if ($a->getLevel() < 0) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_NEGATIVE_REGISTER);
            } else {
                if (false === $a->delete()) {
                    throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_DELETE, [
                        'message' => join('. ', $a->getMessages()),
                    ]);
                }
            }
        } else {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_ACCESS_NOT_FOUND);
        }
 
        $this->createHistory($c->getName(), History::EVENT_DELACCESS, $ip, [
            'nick' => $nick,
        ]);
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->createHistory($nick, History::EVENT_DELACCESS, $ip, [
            'channel' => $channel,
        ]);
 
        $this->enqueue($c);
 
        $discourse_manager = $this->getDI()->get('discourse_manager');
        $discourse_manager->updateChannel($channel);
 
        $dbTx->commit();
 
        return $a;
    }
 
    public function getStatusAll()
    {
        return Status::find();
    }
 
    public function getStatusList($status, $page = 1)
    {
        return Status::find([
            [
                'status' => (int)$status,
            ],
            'limit' => self::PAGE_SIZE,
            'skip' => ($page - 1) * self::PAGE_SIZE,
        ]);
    }
 
    public function getStatus($channel)
    {
        return Status::findFirst([
            [
                'channel' => mb_strtolower($channel),
            ],
        ]);
    }
 
    public function setStatus($channel, $status, $ip, $nick, $reason = '', $password = '', $description = '', $email = '')
    {
        $o = $this->getStatus($channel);
        if (!$o) {
            $o = new Status();
            $o->channel = mb_strtolower($channel);
        }
        $o->ip = $ip;
        $o->nick = mb_strtolower($nick);
        $o->status = $status;
        if ($o->status == Status::STATUS_REQUESTED) {
            $o->founder = $o->nick;
        }
        $o->ts = time();
        $o->reason = $reason;
        $o->password = $password;
        $o->description = $description;
        $o->email = $email;
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_UPDATE, [
                'message' => join('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function comment($channel, $nick, $ip, $comment)
    {
        return $this->createHistory($channel, History::EVENT_COMMENT, $ip, [
            'who' => $nick,
            'comment' => $comment,
        ]);
    }
 
    public function getIdentified($channel)
    {
        $redis = $this->getDI()->get('redis_cluster');
        $json = $redis->hGet(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel));
        if ($json) {
            $nicks = json_decode($json, true);
        } else {
            $nicks = [];
        }
 
        return $nicks;
    }
 
    public function isIdentified($channel, $nick)
    {
        $redis = $this->getDI()->get('redis_cluster');
        $json = $redis->hGet(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel));
        if ($json) {
            $nicks = json_decode($json, true);
        } else {
            $nicks = [];
        }
 
        return in_array(mb_strtolower($nick), $nicks);
    }
 
    public function identify($channel, $password, $nick, $ip)
    {
        $nick = mb_strtolower($nick);
 
        $o = $this->get($channel, true);
 
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        if ($o->getFounder() == mb_strtolower($nick)) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_ALREADY_FOUNDER);
        }
 
        if (false === $this->checkLevel($o, 'IDENTIFY', $nick)) {
            throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
        }
 
        if (!$this->getDI()->get('security')->checkHash($password, $o->getPassword())) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_BAD_PASSWORD);
        }
 
        $this->setIdentify($channel, $nick, true);
 
        $this->enqueueIdentify($o, $nick, "1");
 
        $this->createHistory($o->getName(), History::EVENT_IDENTIFY, $ip, [
            'who' => $nick,
        ]);
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        $nick_manager->createHistory($nick, History::EVENT_IDENTIFY, $ip, [
            'name' => $o->getName(),
        ]);
    }
 
    public function setIdentify($channel, $nick, $triggerException = false)
    {
        $redis = $this->getDI()->get('redis_cluster');
        $json = $redis->hGet(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel));
        if ($json) {
            $nicks = json_decode($json, true);
            if (in_array($nick, $nicks)) {
                if ($triggerException == true) {
                    throw new Exception(null, ErrorCodes::ERR_CHANNEL_NICK_ALREADY_IDENTIFIED);
                } else {
                    return;
                }
            }
        } else {
            $nicks = [];
        }
        $nicks[] = $nick;
        $redis->hSet(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel), json_encode($nicks));
    }
 
    public function unidentify($channel, $nick)
    {
        $nick = mb_strtolower($nick);
 
        $o = $this->get($channel, true);
 
        $redis = $this->getDI()->get('redis_cluster');
        $json = $redis->hGet(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel));
        if ($json) {
            $nicks = json_decode($json, true);
            if (!in_array($nick, $nicks)) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_NICK_NOT_IDENTIFIED);
            }
            $nicks = array_diff($nicks, [$nick]);
            $redis->hSet(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel), json_encode($nicks));
        }
 
        $this->enqueueIdentify($o, $nick, "0");
    }
 
    public function unidentifyChannel($channel)
    {
        $o = $this->get($channel, true);
 
        // NOTE: We get the identified list before delete to send to Socket later
        $list = $this->getIdentified($channel);
 
        $redis = $this->getDI()->get('redis_cluster');
        $redis->hDel(self::REDIS_HASH_CHANNEL_IDENTIFY, mb_strtolower($channel));
 
        // NOTE: We send to socket the unidentified list
        foreach ($list as $nick) {
            $this->enqueueIdentify($o, $nick, "0");
        }
    }
 
    public function unidentifyAll()
    {
        $redis = $this->getDI()->get('redis_cluster');
        $redis->del(self::REDIS_HASH_CHANNEL_IDENTIFY);
        // NOTE: We do not send to Socket because this is only a services burst reset
    }
 
    public function info($bot, $channel, $nick, $ip, $oper = false)
    {
        if ($oper === true) {
            $nick_manager = $this->getDI()->get('inspircd_nick_manager');
            $nick_manager->checkOperator($nick, 'preoper');
        }
 
        $s = $this->getStatus($channel);
        if (!$s) {
            $s = new Status();
            $s->channel = mb_strtolower($channel);
            $s->status = Status::STATUS_NOT_REGISTERED;
        }
        $messages = [
            $bot->buildMessage("Information from channel %channel%:", [
                'channel' => $channel,
            ]),
            "    ".$bot->buildMessage("Status").": ".$bot->buildMessage($s->getStatusName()),
        ];
        try {
            $o = $this->get($channel, true);
            if ($o->getIsSuspended() == 1) {
                $messages[] = "    ".$bot->buildMessage("Suspend reason").": ".$o->getSuspendReason();
                if ($oper === true) {
                    $messages[] = "    ".$bot->buildMessage("Suspended by").": ".$o->getSuspendBy();
                }
                $messages[] = "    ".$bot->buildMessage("Suspended until").": ".date('Y-m-d H:i:s', $o->getSuspendTs() + $o->getSuspendDuration());
            }
            $messages[] = "    ".$bot->buildMessage("Section").": ".(strlen($o->getSection()) <= 0 ? "-" : $o->getSection());
            if ($o->getIg() == 1) {
                $messages[] = "    ".$bot->buildMessage("It is a general interest channel.", []);
            }
            $messages[] = "    ".$bot->buildMessage("Founder").": ".($o->getFounder() ? $o->getFounder() : $bot->buildMessage("Without founder"));
            if ($o->getSuccessor()) {
                $messages[] = "    ".$bot->buildMessage("Successor").": ".$o->getSuccessor();
            }
            $messages[] = "    ".$bot->buildMessage("Description").": ".$o->getDescription();
            $messages[] = "    ".$bot->buildMessage("Registered").": ".date('Y-m-d H:i:s', $o->getTimeRegistered());
            $messages[] = "    ".$bot->buildMessage("Last use").": ".date('Y-m-d H:i:s', $o->getTimeLastUse());
            $options = $o->getOptions(!$oper);
            if (($options['noexpire'] === false || $options['noexpire'] === 0) && $o->getIg() != 1) {
                if ($o->getTimeLastUse()) {
                    $messages[] = "    ".$bot->buildMessage("Expires at").": ".date('Y-m-d H:i:s', $o->getTimeLastUse() + self::CHANNEL_EXPIRE_DAYS * 24 * 3600);
                } else {
                    $messages[] = "    ".$bot->buildMessage("Expires at").": ".date('Y-m-d H:i:s', $o->getTimeRegistered() + self::CHANNEL_EXPIRE_DAYS * 24 * 3600);
                }
            }
            $levels = $this->normalizeLevels(json_decode($o->getLevels(), true));
            // We only show topic and entrymsg information if user is an operator or is not blocked by NOJOIN level
            if ($oper === true || false === $this->checkLevel($channel, 'NOJOIN', $nick)) {
                if (strlen($o->getLastTopic()) > 0) {
                    $messages[] = "    ".$bot->buildMessage("Last topic").": ".$o->getLastTopic();
                    $messages[] = "    ".$bot->buildMessage("Topic set by").": ".$o->getLastTopicBy();
                    $messages[] = "    ".$bot->buildMessage("Topic set on").": ".date('Y-m-d H:i:s', $o->getLastTopicTime());
                } else {
                    $messages[] = "    ".$bot->buildMessage("No topic set");
                }
                if (strlen($o->getEntrymsg())) {
                    $messages[] = "    ".$bot->buildMessage("Entry message").": ".$o->getEntrymsg();
                } else {
                    $messages[] = "    ".$bot->buildMessage("No entry message set");
                }
            }
            if ($o->getUrl()) {
                $messages[] = "    URL: ".$o->getUrl();
            }
            try {
                $blog_manager = $this->getDI()->get('blog_manager');
                if ($blog_manager->countPosts($o->getName()) > 0) {
                    $config = $this->getDI()->get('config');
                    $messages[] = "    ".$bot->buildMessage("Blog").": ".$config->application->baseUri.'canal/'.$o->getNamePretty().'/blog';
                }
            } catch (\Exception $e) {
            }
            $allopts = $o->getOptions(false);
            if ($allopts['forum'] === true) {
                $config = $this->getDI()->get('config');
                $s = $o->getSectionObj();
                if ($s) {
                    $messages[] = "    ".$bot->buildMessage("Forum").": ".$config->discourse->baseUri.'/c/'.$s->getSlug().'/'.$o->getNamePretty();
                }
            }
            if ($o->getEmail()) {
                $messages[] = "    ".$bot->buildMessage("Email").": ".$o->getEmail();
            }
            $messages[] = "    ".$bot->buildMessage("Options").":";
            foreach ($options as $opt => $v) {
                $messages[] = "        ".mb_strtoupper($opt).": ".$bot->buildMessage($v ? "Activated" : "Deactivated", []);
            }
            $messages[] = "    ".$bot->buildMessage("Modes lock").": ".$o->getMlock();
        } catch (\Exception $e) {
        }
        $messages[] = $bot->buildMessage("End of information from channel %channel%.", [
            'channel' => $channel,
        ]);
 
        return $messages;
    }
 
    public function checkAccess($channel, $identity)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $c = $irc_manager->getChannel($channel);
        if ($c && isset($c['modes']) && preg_match('/(s|p)/', $c['modes'])) {
            $o = $this->get($channel, true);
            if ($o && mb_strtolower($o->getFounder()) == mb_strtolower($identity['selected_nick'])) {
                return;
            }
            $a = Access::findFirst([
                'channel = :channel: AND nick = :nick:',
                'bind' => [
                    'channel' => mb_strtolower($channel),
                    'nick' => (isset($identity['selected_nick']) ? mb_strtolower($identity['selected_nick']) : ''),
                ],
            ]);
            if ($a && $a->getLevel() > 0) {
                return;
            }
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_IS_PRIVATE);
        }
    }
 
    public function getChannelsBySection($section, $limit = -1, $exclude = '')
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
 
        $minimum_nicks = $this->getDI()->get('config')->channels->important->minimum_nicks;
 
        $list = Channel::find([
            'section = :section:',
            'bind' => [
                'section' => $section,
            ],
        ]);
 
        $temp = [];
        foreach ($list as $o) {
            if ($o->getIsSuspended() != 1) {
                $obj = $irc_manager->getChannel($o->getName());
                $sheet = $o->getDatasheet();
                if ($sheet || $obj && isset($obj['modes']) && !preg_match('/(i|R|s|p|k)/', $obj['modes'])) {
                    if (mb_strtolower($o->getName()) != mb_strtolower($exclude)) {
                        $c = $obj && isset($obj['cnt_members']) ? $obj['cnt_members'] : 0;
                        if (!isset($temp[$c])) {
                            $temp[$c] = [];
                        }
                        $temp[$c][] = $o;
                    }
                }
            }
        }
        ksort($temp, SORT_NUMERIC);
        $temp = array_reverse($temp, true);
 
        $channels = [];
        foreach ($temp as $i => $list) {
            foreach ($list as $o) {
                if ($i >= $minimum_nicks || $o->getDatasheet()) {
                    $channels[] = [
                        'obj' => $o,
                        'users' => $i,
                    ];
                }
            }
        }
 
        if ($limit > 0) {
            $channels = array_slice($channels, 0, $limit);
        }
 
        return $channels;
    }
 
    public function getRandomDatasheet()
    {
        $list = Datasheet::find([
            'order' => 'RAND() DESC',
            'limit' => 10,
        ]);
 
        foreach ($list as $o) {
            $c = $o->getChannelObj();
            if ($c && $c->getSection() != 'ADU' && $c->getSection() != 'GAY' && $c->getSection() != 'LES') {
                return $o;
            }
        }
 
        return false;
    }
 
    public function createChannelSection($request)
    {
        $default = [
            'parent' => new \Phalcon\Db\RawValue(''),
        ];
 
        $dbTx = (new PhalconTxManager())->get();
 
        $o = new ChannelSection();
        if (is_array($request)) {
            $o->assign(array_merge($default, $request));
        } else {
            $o->assign(array_merge($default, $request->getPost()));
        }
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_CREATE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        if (!is_array($request)) {
            $this->updateChannelSectionImages($request, true);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function saveChannelSection($request)
    {
        $default = [
            'parent' => new \Phalcon\Db\RawValue(''),
        ];
 
        $dbTx = (new PhalconTxManager())->get();
 
        if (is_array($request)) {
            $o = $this->getSection(isset($request['code']) ? $request['code'] : '');
            $o->assign(array_merge($default, $request));
        } else {
            $o = $this->getSection($request->getPost('code', 'string', ''));
            $o->assign(array_merge($default, $request->getPost()));
        }
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_UPDATE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        if (!is_array($request)) {
            $this->updateChannelSectionImages($request, false);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    protected function updateChannelSectionImages($request, $require_files = false)
    {
        if (count($request->getUploadedFiles()) < 1) {
            if (true === $require_files) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_SECTION_CREATE, [
                    'message' => 'You missed to upload images.',
                ]);
            } else {
                return;
            }
        }
 
        $cdn_config = $this->getDI()->get('config')->cdn->toArray();
        $env = trim(file_get_contents(__DIR__.'/../../../../config/environment.txt'));
 
        foreach ($request->getUploadedFiles() as $file) {
            if ($file->getName() == '') {
                continue;
            }
            if ($file->getKey() == 'image_off' || $file->getKey() == 'image_on') {
                if ($file->getKey() == 'image_off') {
                    $fname = $cdn_config['path'].'/channels/sections/'.$request->getPost('code', 'string', '').'-off.png';
                } else {
                    $fname = $cdn_config['path'].'/channels/sections/'.$request->getPost('code', 'string', '').'-on.png';
                }
                Graphics::resizeW($file->getTempName(), $file->getTempName(), 1024);
                Graphics::crop($file->getTempName(), $file->getTempName(), 0, 0, 1024, 520);
                $file->moveTo($fname);
            }
        }
 
        if ($env == 'prod') {
            foreach ($request->getUploadedFiles() as $file) {
                if ($file->getName() == '') {
                    continue;
                }
                if ($file->getKey() == 'image_off' || $file->getKey() == 'image_on') {
                    if ($file->getKey() == 'image_off') {
                        $fname = $cdn_config['path'].'/channels/sections/'.$request->getPost('code', 'string', '').'-off.png';
                        $name = $request->getPost('section', 'string', '').'-off.png';
                    } else {
                        $fname = $cdn_config['path'].'/channels/sections/'.$request->getPost('code', 'string', '').'-on.png';
                        $name = $request->getPost('section', 'string', '').'-on.png';
                    }
                    foreach ($cdn_config['nodes'] as $node) {
                        if ($this->request->getServerAddress() != $node['host']) {
                            try {
                                $cdn = new Cdn($node['host'], $node['user'], $node['passwd'], 'channels/sections');
                                $cdn->connect();
                                $cdn->upload([
                                    $fname => $file->getName(),
                                ]);
                                $cdn->disconnect();
                            } catch (\Exception $e) {
                                $this->getDI()->get('redis_cluster')->rPush('chathispano_ftp_pending', json_encode([
                                    'server' => $node['host'],
                                    'user' => $node['user'],
                                    'passwd' => $node['passwd'],
                                    'path' => $fname,
                                    'folder' => 'channels/sections',
                                    'name' => $name,
                                ]));
                            }
                        }
                    }
                }
            }
        }
    }
 
    public function getDatasheets()
    {
        return Datasheet::find([
            'order' => 'channel ASC',
        ]);
    }
 
    public function getDatasheet($channel)
    {
        $o = Datasheet::findFirstByChannel($channel);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_NOT_FOUND, [
                'name' => $channel,
            ]);
        }
 
        return $o;
    }
 
    public function getDatasheetBySlug($slug)
    {
        $o = Datasheet::findFirstBySlug($slug);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_NOT_FOUND, [
                'name' => $slug,
            ]);
        }
 
        return $o;
    }
 
    public function getRandomDatasheets($amount = 10)
    {
        return Datasheet::find([
            "section <> 'ADU' AND section <> 'GAY' AND section <> 'LES'",
            'order' => 'RAND() DESC',
            'limit' => $amount,
        ]);
    }
 
    public function createChannelDatasheet($request)
    {
        if (is_array($request)) {
            $c = Channel::findFirstByName(isset($request['channel']) ? $request['channel'] : '');
        } else {
            $c = Channel::findFirstByName($request->getPost('channel', 'string', ''));
        }
        if (!$c) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_REQUIRE_REGISTERED_CHANNEL);
        }
 
        $dbTx = (new PhalconTxManager())->get();
 
        $o = new Datasheet();
        if (is_array($request)) {
            $o->assign($request);
        } else {
            $o->assign($request->getPost());
        }
        $o->setSection($c->getSection());
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_CREATE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        if (!is_array($request)) {
            $this->updateChannelDatasheetImage($request, true);
        }
 
        $dbTx->commit();
 
        return $o;
    }
 
    public function saveChannelDatasheet($request)
    {
        if (!is_array($request)) {
            $dbTx = (new PhalconTxManager())->get();
        }
 
        if (is_array($request)) {
            $o = $this->getDatasheetBySlug(isset($request['slug']) ? $request['slug'] : '');
            $o->assign($request);
        } else {
            $o = $this->getDatasheetBySlug($request->getPost('slug', 'string', ''));
            $o->assign($request->getPost());
        }
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_UPDATE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        if (!is_array($request)) {
            $this->updateChannelDatasheetImage($request, false);
            $dbTx->commit();
        }
 
        return $o;
    }
 
    public function dropChannelDatasheet($slug)
    {
        $o = $this->getDatasheetBySlug($slug);
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_DELETE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    protected function updateChannelDatasheetImage($request, $require_files = false)
    {
        if (count($request->getUploadedFiles()) < 1) {
            if (true === $require_files) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_CREATE, [
                    'message' => 'You missed to upload images.',
                ]);
            } else {
                return;
            }
        }
 
        $cdn_config = $this->getDI()->get('config')->cdn->toArray();
        $path = $cdn_config['path'].'/channels/datasheet/';
        $env = trim(file_get_contents(__DIR__.'/../../../../config/environment.txt'));
 
        foreach ($request->getUploadedFiles() as $file) {
            if ($file->getName() == '') {
                continue;
            }
            if ($file->getKey() == 'image') {
                Graphics::resizeW($file->getTempName(), $file->getTempName(), 1024);
                $fname = mb_strtolower($request->getPost('slug', 'string', '')).'.png';
                $file->moveTo($path.$fname);
            }
        }
 
        if ($env == 'prod') {
            foreach ($request->getUploadedFiles() as $file) {
                if ($file->getName() == '') {
                    continue;
                }
                if ($file->getKey() == 'image') {
                    $fname = mb_strtolower($request->getPost('slug', 'string', '')).'.png';
                    foreach ($cdn_config['nodes'] as $node) {
                        if ($request->getServerAddress() != $node['host']) {
                            try {
                                $cdn = new Cdn($node['host'], $node['user'], $node['passwd'], 'channels/datasheet');
                                $cdn->connect();
                                $cdn->upload([
                                    $path.$fname => $fname,
                                ]);
                                $cdn->disconnect();
                            } catch (\Exception $e) {
                                $this->getDI()->get('redis_cluster')->rPush('chathispano_ftp_pending', json_encode([
                                    'server' => $node['server'],
                                    'user' => $node['user'],
                                    'passwd' => $node['passwd'],
                                    'path' => $path.$fname,
                                    'folder' => 'channels/datasheet',
                                    'name' => $fname,
                                ]));
                            }
                        }
                    }
                }
            }
        }
    }
 
    public function getOverrides()
    {
        return Override::find([
            'order' => 'channel ASC',
        ]);
    }
 
    public function getOverride($channel)
    {
        $o = Override::findFirstByChannel($channel);
 
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_NOT_FOUND, [
                'name' => $channel,
            ]);
        }
 
        return $o;
    }
 
    public function createChannelOverride($request)
    {
        if (is_array($request)) {
            $o = Channel::findFirstByName(isset($request['channel']) ? $request['channel'] : '');
        } else {
            $o = Channel::findFirstByName($request->getPost('channel', 'string', ''));
        }
        if (!$o) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_REQUIRE_REGISTERED_CHANNEL);
        }
 
        $o = new Override();
        if (is_array($request)) {
            $o->assign($request);
        } else {
            $o->assign($request->getPost());
        }
 
        if (false === $o->create()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_CREATE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function saveChannelOverride($request)
    {
        if (is_array($request)) {
            $o = $this->getOverride(isset($request['channel']) ? $request['channel'] : '');
            $o->assign($request);
        } else {
            $o = $this->getOverride($request->getPost('channel', 'string', ''));
            $o->assign($request->getPost());
        }
        if (false === $o->save()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_UPDATE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function dropChannelOverride($channel)
    {
        $o = $this->getOverride($channel);
 
        if (false === $o->delete()) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_DATASHEET_DELETE, [
                'message' => implode('. ', $o->getMessages()),
            ]);
        }
 
        return $o;
    }
 
    public function getBlogsData(User $o)
    {
        $data = [];
 
        $blog_manager = $this->getDI()->get('blog_manager');
        $nicks = $o->getNicks();
        foreach ($nicks as $n) {
            $channels = $n->getFounderChannels();
            foreach ($channels as $c) {
                // We skip if we have already granted access
                if (isset($data[$c->getName()])) {
                    continue;
                }
                // It does not have access to post
                if (!$this->checkBlogger($c, $n->getNick())) {
                    continue;
                }
                $last_post = $blog_manager->getLastChannelPost($c->getName(), true);
                $data[$c->getNamePretty()] = [
                    'name' => $c->getName(),
                    'pretty_name' => $c->getNamePretty(),
                    'total_posts' => $blog_manager->countPosts($c->getName(), true),
                    'published_posts' => $blog_manager->countPosts($c->getName(), false),
                    'last_post_date' => $last_post ? $last_post->getUpdatedAt() : '-',
                ];
            }
            $channels = $n->getSuccessorChannels();
            foreach ($channels as $c) {
                // We skip if we have already granted access
                if (isset($data[$c->getName()])) {
                    continue;
                }
                // It does not have access to post
                if (!$this->checkBlogger($c, $n->getNick())) {
                    continue;
                }
                $last_post = $blog_manager->getLastChannelPost($c->getName(), true);
                $data[$c->getNamePretty()] = [
                    'name' => $c->getName(),
                    'pretty_name' => $c->getNamePretty(),
                    'total_posts' => $blog_manager->countPosts($c->getName(), true),
                    'published_posts' => $blog_manager->countPosts($c->getName(), false),
                    'last_post_date' => $last_post ? $last_post->getUpdatedAt() : '-',
                ];
            }
            $channels = $n->getChannelRegisters();
            foreach ($channels as $r) {
                // We skip if we have already granted access
                if (isset($data[$r->getChannel()])) {
                    continue;
                }
                // It does not have access to post
                if (!$this->checkBlogger($r->getChannel(), $n->getNick())) {
                    continue;
                }
                $last_post = $blog_manager->getLastChannelPost($r->getChannel(), true);
                $data[$r->getChannelPretty()] = [
                    'name' => $r->getChannel(),
                    'pretty_name' => $r->getChannelPretty(),
                    'total_posts' => $blog_manager->countPosts($r->getChannel(), true),
                    'published_posts' => $blog_manager->countPosts($r->getChannel(), false),
                    'last_post_date' => $last_post ? $last_post->getUpdatedAt() : '-',
                ];
            }
        }
 
        return $data;
    }
 
    public function getForumsData(User $o)
    {
        $data = [];
 
        $nicks = $o->getNicks();
        foreach ($nicks as $n) {
            $channels = $n->getFounderChannels();
            foreach ($channels as $c) {
                // We skip if we have already granted access
                if (isset($data[$c->getName()])) {
                    continue;
                }
                // It does not have access to post
                if (!$this->checkForum($c, $n->getNick())) {
                    continue;
                }
                $options = $c->getOptions();
                $data[$c->getNamePretty()] = [
                    'name' => $c->getName(),
                    'pretty_name' => $c->getNamePretty(),
                    'activated' => $options && is_array($options) && isset($options['forum']) ? $options['forum'] : false,
                    'section_slug' => $c->getSectionObj() ? $c->getSectionObj()->getSlug() : '',
                ];
            }
            $channels = $n->getSuccessorChannels();
            foreach ($channels as $c) {
                // We skip if we have already granted access
                if (isset($data[$c->getName()])) {
                    continue;
                }
                // It does not have access to post
                if (!$this->checkForum($c, $n->getNick())) {
                    continue;
                }
                $options = $c->getOptions();
                $data[$c->getNamePretty()] = [
                    'name' => $c->getName(),
                    'pretty_name' => $c->getNamePretty(),
                    'activated' => $options && is_array($options) && isset($options['forum']) ? $options['forum'] : false,
                    'section_slug' => $c->getSectionObj() ? $c->getSectionObj()->getSlug() : '',
                ];
            }
            $channels = $n->getChannelRegisters();
            foreach ($channels as $r) {
                // We skip if we have already granted access
                if (isset($data[$r->getChannel()])) {
                    continue;
                }
                // It does not have access to post
                if (!$this->checkForum($r->getChannel(), $n->getNick())) {
                    continue;
                }
                $c = $r->getChannelObj();
                $options = $c->getOptions();
                $data[$r->getChannelPretty()] = [
                    'name' => $r->getChannel(),
                    'pretty_name' => $r->getChannelPretty(),
                    'activated' => $options && is_array($options) && isset($options['forum']) ? $options['forum'] : false,
                    'section_slug' => $c->getSectionObj() ? $c->getSectionObj()->getSlug() : '',
                ];
            }
        }
 
        return $data;
    }
 
    public function activateForum($name, $nick, $ip)
    {
        $o = $this->get($name, true);
        if ($o->getIsSuspended() == 1) {
            throw new Exception(null, ErrorCodes::ERR_CHANNEL_SUSPENDED);
        }
 
        $nick_manager = $this->getDI()->get('inspircd_nick_manager');
        if (false === $this->checkLevel($o, 'FORUM', $nick)) {
            try {
                $nick_manager->checkOperator($nick, 'devel');
            } catch (\Exception $e) {
                throw new Exception(null, ErrorCodes::ERR_ACCESS_DENIED);
            }
        }
 
        try {
            $discourse_manager = $this->getDI()->get('discourse_manager');
            $discourse_manager->createChannel($o);
            $this->setOption($name, 'ChatHispano', 'forum', 'on', '127.0.0.1', false);
        } catch (\Exception $e) {
            throw new Exception(null, ErrorCodes::ERR_SERVICES_UNKNOWN, [
                'code' => $e->getMessage(),
            ]);
        }
 
        return $o;
    }
 
    protected function expireAkickList($list, $type, &$updatedChannels)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $redis = $this->getDI()->get('redis_cluster');
        foreach ($list as $o) {
            try {
                if (false === $o->delete()) {
                    $msg = "\x02\x034[ERROR] [AKICK] [EXPIRE]\x03 Channel=\x02 ".$o->getChannel()."\x02 Mask=\x02 ".$o->getMask()."\x02 Message=\x02 ".join(". ", $o->getMessages());
                    $irc_manager->privmsgOrderEnqueue('AAAAAA', '#debug', $msg);
                } else {
                    $redis->set('chathispano_inspircd_akick_delete_'.str_replace('#', '', $o->getChannel()).'_'.$o->getMask(), 1, 3600 * 24);
                    if (!in_array($o->getChannel(), $updatedChannels)) {
                        $updatedChannels[] = $o->getChannel();
                    }
                    $msg = "\x02[AKICK] [EXPIRE] Channel=\x02 ".$o->getChannel()."\x02 Mask=\x02 ".$o->getMask()."\x02 Date=\x02 ".$o->getCreatedOn()."\x02 Hits=\x02 ".$o->getHits()."\x02 LastHit=\x02 ".$o->getLastHit()."\x02 Type=\x02 ".$type;
                    $irc_manager->privmsgOrderEnqueue('AAAAAA', '#chan', $msg);
                    $irc_manager->noticeOrderEnqueue('AAAAAA', '@'.$o->getChannel(), "El akick ".$o->getMask()." ha expirado");
                    $this->createHistory($o->getChannel(), History::EVENT_AKICK_EXPIRE, '127.0.0.1', [
                        $o->getMask(),
                    ]);
                }
            } catch (\Exception $e) {
                $msg = "\x02\x034[ERROR] [AKICK] [EXPIRE]\x03 Channel=\x02 ".$o->getChannel()."\x02 Mask=\x02 ".$o->getMask()."\x02 Message=\x02 ".$e->getMessage();
                $irc_manager->privmsgOrderEnqueue('AAAAAA', '#debug', $msg);
            }
        }
    }
 
    protected function expireNegativeRegisters($list, $type, &$updatedChannels)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $redis = $this->getDI()->get('redis_cluster');
        foreach ($list as $o) {
            try {
                if (false === $o->delete()) {
                    $msg = "\x02\x034[ERROR] [ACCESS] [EXPIRE]\x03 Channel=\x02 ".$o->getChannel()."\x02 Nick=\x02 ".$o->getNick()."\x02 Message=\x02 ".join(". ", $o->getMessages());
                    $irc_manager->privmsgOrderEnqueue('AAAAAA', '#debug', $msg);
                } else {
                    $redis->set('chathispano_inspircd_negative_registers_delete_'.str_replace('#', '', $o->getChannel()).'_'.$o->getNick(), 1, 3600 * 24);
                    if (!in_array($o->getChannel(), $updatedChannels)) {
                        $updatedChannels[] = $o->getChannel();
                    }
                    $msg = "\x02[ACCESS] [EXPIRE] Channel=\x02 ".$o->getChannel()."\x02 Nick=\x02 ".$o->getNick()."\x02 Date=\x02 ".date('Y-m-d H:i:s', $o->getSetOn())."\x02 LastHit=\x02 ".date('Y-m-d H:i:s', $o->getLastUse())."\x02 Type=\x02 ".$type;
                    $irc_manager->privmsgOrderEnqueue('AAAAAA', '#chan', $msg);
                    $irc_manager->noticeOrderEnqueue('AAAAAA', '@'.$o->getChannel(), "El registro -1 ".$o->getNick()." ha expirado");
                    $this->createHistory($o->getChannel(), History::EVENT_ACCESS_EXPIRE, '127.0.0.1', [
                        $o->getNick(),
                    ]);
                }
            } catch (\Exception $e) {
                $msg = "\x02\x034[ERROR] [ACCESS] [EXPIRE]\x03 Channel=\x02 ".$o->getChannel()."\x02 Nick=\x02 ".$o->getNick()."\x02 Message=\x02 ".$e->getMessage();
                $irc_manager->privmsgOrderEnqueue('AAAAAA', '#debug', $msg);
            }
        }
    }
 
    public function expireAkicks()
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $updatedChannels = [];
        // Expire akicks with no use and 7 days age
        $list = Akick::find([
            "hits = 0 AND created_on < :date:",
            "bind" => [
                "date" => date('Y-m-d H:i:s', strtotime("now -7 days")),
            ],
            "order" => "channel ASC",
        ]);
        $this->expireAkickList($list, 'NOHITS', $updatedChannels);
        // Expire akicks with no hits in the last 30 days
        $list = Akick::find([
            "hits > 0 AND last_hit < :date:",
            "bind" => [
                "date" => date('Y-m-d H:i:s', strtotime("now -30 days")),
            ],
            "order" => "channel ASC",
        ]);
        $this->expireAkickList($list, 'NOHITS30D', $updatedChannels);
        // Expire akicks with 90 days age
        $list = Akick::find([
            "created_on < :date:",
            "bind" => [
                "date" => date('Y-m-d H:i:s', strtotime("now -90 days")),
            ],
            "order" => "channel ASC",
        ]);
        $this->expireAkickList($list, '90DOLD', $updatedChannels);
        $list = Access::find([
            'level = -1 AND set_on < :date: AND last_use IS NULL',
            'bind' => [
                "date" => strtotime("now -7 days"),
            ],
            'order' => 'channel ASC',
        ]);
        $this->expireNegativeRegisters($list, 'NOHITS', $updatedChannels);
        $list = Access::find([
            'level = -1 AND last_use IS NOT NULL AND last_use < :date:',
            'bind' => [
                "date" => strtotime("now -30 days"),
            ],
            'order' => 'channel ASC',
        ]);
        $this->expireNegativeRegisters($list, 'NOHITS30D', $updatedChannels);
        $list = Access::find([
            'level = -1 AND set_on < :date:',
            'bind' => [
                "date" => strtotime("now -90 days"),
            ],
            'order' => 'channel ASC',
        ]);
        $this->expireNegativeRegisters($list, '90OLD', $updatedChannels);
        // Send updated channels to servers
        foreach ($updatedChannels as $channel) {
            $o = Channel::findFirst([
                "name = :name:",
                "bind" => [
                    "name" => $channel,
                ],
            ]);
            if (!$o) {
                $msg = "\x02\x034[ERROR] [AKICK] [EXPIRE]\x03 Channel=\x02 ".$channel."\x02 Message=\x02 Channel not found";
                $irc_manager->privmsgOrderEnqueue('AAAAAA', '#debug', $msg);
            } else {
                $this->enqueue($o);
            }
        }
    }
 
    protected function expireSuspend(Channel $o)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        try {
            $this->update($o, [
                'is_suspended' => 0,
                'suspend_by' => new \Phalcon\Db\RawValue("NULL"),
                'suspend_reason' => new \Phalcon\Db\RawValue("NULL"),
                'suspend_ts' => new \Phalcon\Db\RawValue("NULL"),
                'suspend_duration' => new \Phalcon\Db\RawValue("NULL"),
            ]);
 
            $discourse_manager = $this->getDI()->get('discourse_manager');
            $discourse_manager->updateChannel($o->getName());
 
            $this->setStatus($o->getName(), Status::STATUS_REGISTERED, '127.0.0.1', $o->getFounder());
 
            $c = $this->get($o->getName(), true);
            $this->createHistory($o->getName(), History::EVENT_EXPIRE_SUSPEND, '127.0.0.1', []);
            $this->enqueue($c);
            $irc_manager = $this->getDI()->get('inspircd_irc_manager');
            $msg = "\x02[SUSPEND] [EXPIRE] Channel=\x02 ".$o->getName();
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $this->getDI()->get('config')->irc->bots->chan->debug_channel, $msg);
 
            $dbTx->commit();
        } catch (\Exception $e) {
            $dbTx->rollback();
        }
    }
 
    public function expireSuspends($console)
    {
        $list = $this->listSuspendExpired();
        $cnt = 0;
        foreach ($list as $o) {
            try {
                $this->expireSuspend($o);
                $cnt++;
            } catch (\Exception $e) {
                $dbTx = (new PhalconTxManager())->get();
                $dbTx->rollback();
            }
        }
        $console->consoleLog(">>> Expired ".$cnt." channel suspends", 'yellow');
    }
 
    protected function expireChannel(Channel $o)
    {
        $dbTx = (new PhalconTxManager())->get();
 
        try {
            $founder = $o->getFounderObj();
 
            if (false === $o->delete()) {
                throw new Exception(null, ErrorCodes::ERR_CHANNEL_DELETE, [
                    'message' => join('. ', $o->getMessages()),
                ]);
            }
 
            $this->setStatus($o->getName(), Status::STATUS_NOT_REGISTERED, '127.0.0.1', 'CHaN');
 
            $this->createHistory($o->getName(), History::EVENT_EXPIRED, '127.0.0.1', []);
 
            $this->enqueueDelete($o);
 
            $irc_manager = $this->getDI()->get('inspircd_irc_manager');
            $msg = "\x02\x034[EXPIRED]\x03 Name=\x02 ".$o->getName()."\x02 Last use=\x02 ".date('Y-m-d H:i:s', $o->getTimeLastUse());
            $irc_manager->privmsgOrderEnqueue('AAAAAA', $this->getDI()->get('config')->irc->bots->chan->debug_channel, $msg);
 
            if ($founder) {
                $mailer = $this->getDI()->get('mail');
                $mailer->addTo($founder->getEmail());
                $mailer->setLanguage('es');
                $mailer->send(
                    'Chat Hispano',
                    '[ChatHispano] El canal '.$o->getName().' ha expirado',
                    'channel_expired',
                    null,
                    [
                        'name' => $o->getName(),
                        'nick' => $founder->getEmail(),
                        'last_use' => date('Y-m-d H:i:s', $o->getTimeLastUse()),
                        'expire_days' => self::CHANNEL_EXPIRE_DAYS,
                        'images' => [
                            'logo' => 'public/assets/backoffice/img/logo-medium.jpg',
                        ],
                    ]
                );
            }
 
            $dbTx->commit();
        } catch (\Exception $e) {
            $dbTx->rollback();
        }
    }
 
    protected function expireWarnChannel(Channel $o)
    {
        $this->setOption($o->getName(), 'ChatHispano', 'expirewarn', 'on', '127.0.0.1', false);
 
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $msg = "\x02\x038[EXPIRE WARN]\x03 Name=\x02 ".$o->getName()."\x02 Last use=\x02 ".date('Y-m-d H:i:s', $o->getTimeLastUse());
        $irc_manager->privmsgOrderEnqueue('AAAAAA', $this->getDI()->get('config')->irc->bots->chan->debug_channel, $msg);
 
        $founder = $o->getFounderObj();
        if ($founder) {
            $mailer = $this->getDI()->get('mail');
            $mailer->addTo($founder->getEmail());
            $mailer->setLanguage('es');
            $mailer->send(
                'Chat Hispano',
                '[ChatHispano] Aviso de expiración del canal '.$o->getName(),
                'channel_expire_warn',
                null,
                [
                    'name' => $o->getName(),
                    'nick' => $founder->getEmail(),
                    'last_use' => date('Y-m-d H:i:s', $o->getTimeLastUse()),
                    'expire_days' => self::CHANNEL_EXPIRE_DAYS,
                    'images' => [
                        'logo' => 'public/assets/backoffice/img/logo-medium.jpg',
                    ],
                ]
            );
        }
    }
 
    public function expire($console)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $expire_ts = time() - (self::CHANNEL_EXPIRE_DAYS * 24 * 3600);
        $expire_warn = time() - (self::CHANNEL_WARN_EXPIRE_DAYS * 24 * 3600);
        $list = Channel::find([
            'time_last_use < :time_last_use:',
            'bind' => [
                'time_last_use' => $expire_warn,
            ],
        ]);
        $cnt = 0;
        $warn = 0;
        $suspended = 0;
        $noexpire = 0;
        foreach ($list as $o) {
            try {
                $options = $o->getOptions();
                if ($o->getIsSuspended() == 1) {
                    /**
                     * We do not expire suspended channels
                     */
                    $suspended++;
                    continue;
                }
                if ($o->getIg() == 1 || $options['noexpire'] === true) {
                    /**
                     * We do not expire IG channels or with NOEXPIRE
                     */
                    $noexpire++;
                    continue;
                }
                if ($o->getTimeLastUse() == null || $o->getTimeLastUse() < $expire_ts) {
                    $this->expireChannel($o);
                    $cnt++;
                } else {
                    if ($options['expirewarn'] === true) {
                        /**
                         * Channel is already warned
                         */
                        continue;
                    }
                    $this->expireWarnChannel($o);
                    $warn++;
                }
            } catch (\Exception $e) {
                $console->consoleLog(">>> Error: ".$e->getMessage()." ".$e->getCode(), 'red');
                $msg = "\x02\x038[ERROR]\x03 Message=\x02 ".$e->getMessage();
                $irc_manager->privmsgOrderEnqueue('AAAAAA', '#debug', $msg);
            }
        }
        $console->consoleLog(">>> Not expiring ".$suspended." SUSPENDED channels", 'green');
        $console->consoleLog(">>> Not expiring ".$noexpire." channels with NOEXPIRE", 'green');
        $console->consoleLog(">>> Total ".$warn." channels warned", 'yellow');
        $console->consoleLog(">>> Total ".$cnt." channels expired", 'red');
    }
 
    protected function enqueueDelete(Channel $o)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $data = [
            'DATATYPE' => 'DELETE',
            'obj' => [
                'obj' => 'CHANNEL',
                'key' => $o->getName(),
            ],
        ];
        $irc_manager->dataEnqueue($data);
    }
 
    public function enqueue(Channel $o)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $data = $o->socketExport();
        $data['DATATYPE'] = 'CHANNEL';
        $irc_manager->dataEnqueue($data);
    }
 
    protected function enqueueIdentify(Channel $o, $nick, $status)
    {
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $n = $irc_manager->getNick($nick);
        if ($n) {
            $irc_manager->dataEnqueue([
                'DATATYPE' => 'IDENTIFY',
                'channel' => $o->getName(),
                'nick' => $nick,
                'status' => $status,
            ], $n['server_name']);
        }
    }
}
#14ChatHispanoEngine\Core\Managers\InspIRCd\ChannelManager->getRandomDatasheets
/srv/ChatHispanoEngine/releases/94/apps/Web/Controllers/BaseController.php (254)
<?php
 
namespace ChatHispanoEngine\Web\Controllers;
 
use ChatHispanoEngine\Core\Controllers\BaseController as CoreController;
use ChatHispanoEngine\Core\Library\Util;
use ChatHispanoEngine\Core\Library\Irc\IP;
use ChatHispanoEngine\Core\Models\InspIRCd\Channel;
 
class BaseController extends CoreController
{
    protected $private_controllers = [
        'profile',
    ];
    protected $private_actions = [
        'paywall' => [
            'checkoutSuccessPaypal',
        ],
    ];
 
    protected $time_start;
 
    protected $enable_moment = false;
    protected $enable_editor = false;
    protected $enable_charts = false;
 
    protected $head_title;
    protected $head_suffix;
    protected $meta_description;
    protected $meta_site_name;
    protected $meta_show_image;
    protected $meta_image;
    protected $meta_twitter_profile;
    protected $meta_facebook_id;
    protected $meta_noindex;
    protected $meta_nofollow;
    protected $meta_article;
    protected $meta_article_tags;
    protected $meta_article_section;
    protected $meta_article_created;
    protected $meta_article_updated;
    protected $rel_canonical;
 
    protected function getDefaultMetaTags()
    {
        $config = $this->getDI()->get('config');
 
        $this->head_title = $config->seo->head_title;
        $this->head_suffix = $config->seo->head_suffix;
        $this->meta_description = $config->seo->meta_description;
        $this->meta_site_name = $config->seo->meta_site_name;
        $this->meta_show_image = true;
        $this->meta_image = $config->seo->meta_image;
        $this->meta_twitter_profile = $config->seo->meta_twitter_profile;
        $this->meta_facebook_id = $config->seo->meta_facebook_id;
        $this->meta_noindex = $this->env == 'prod' ? false : true;
        $this->meta_nofollow = false;
        $this->article = false;
        $this->rel_canonical = ($this->request->getServer('HTTPS') ? 'https://' : 'http://')
            .$this->request->getServer('SERVER_NAME')
            .($this->request->getServer('REQUEST_URI') == '/' ? '' : $this->request->getServer('REQUEST_URI'));
 
        $controller = $this->dispatcher->getControllerName();
        $action = $this->dispatcher->getActionName();
 
        $seo = $config->seo->controllers->toArray();
        if (isset($seo[$controller]) && isset($seo[$controller][$action])) {
            $this->head_title = $seo[$controller][$action]['title'];
            $this->meta_description = $seo[$controller][$action]['description'];
            if (isset($seo[$controller][$action]['nofollow']) && true === $seo[$controller][$action]['nofollow']) {
                $this->setMetaNofollow(true);
            }
        }
    }
 
    protected function setArticleMeta(array $meta_article_tags, $meta_article_section, $meta_article_created, $meta_article_updated)
    {
        $this->meta_article = true;
        $this->meta_article_tags = $meta_article_tags;
        $this->meta_article_section = $meta_article_section;
        $this->meta_article_created = $meta_article_created;
        $this->meta_article_updated = $meta_article_updated;
    }
 
    protected function setHeadTitle($head_title)
    {
        $this->head_title = $head_title;
    }
 
    protected function setMetaDescription($meta_description)
    {
        $this->meta_description = $meta_description;
    }
 
    protected function setMetaSiteName($meta_site_name)
    {
        $this->meta_site_name = $meta_site_name;
    }
 
    protected function setMetaImage($meta_image)
    {
        $this->meta_image = $meta_image;
    }
 
    protected function setMetaNofollow($meta_nofollow)
    {
        $this->meta_nofollow = $meta_nofollow;
    }
 
    protected function setMetaNoindex($meta_noindex)
    {
        $this->meta_noindex = $meta_noindex;
    }
 
    protected function setRelCanonical($rel_canonical)
    {
        $this->rel_canonical = $rel_canonical;
    }
 
    protected function setMetaTags()
    {
        $this->view->head_title = $this->head_title;
        $this->view->head_suffix = $this->head_suffix;
        $this->view->meta_description = $this->meta_description;
        $this->view->meta_site_name = $this->meta_site_name;
        $this->view->meta_show_image = $this->meta_show_image;
        $this->view->meta_image = $this->meta_image;
        $this->view->meta_facebook_id = $this->meta_facebook_id;
        $this->view->meta_twitter_profile = $this->meta_twitter_profile;
        $this->view->meta_noindex = $this->meta_noindex;
        $this->view->meta_nofollow = $this->meta_nofollow;
        $this->view->meta_article = $this->meta_article;
        if ($this->meta_article) {
            $this->view->meta_article_tags = $this->meta_article_tags;
            $this->view->meta_article_section = $this->meta_article_section;
            $this->view->meta_article_created = $this->meta_article_created;
            $this->view->meta_article_updated = $this->meta_article_updated;
        }
        $this->view->rel_canonical = $this->rel_canonical;
    }
 
    public function beforeExecuteRoute($dispatcher)
    {
        $this->view->cdn_uri = $this->config->cdn->baseUri;
        $this->translate->setDefaultDomain('web');
 
        $controller = $this->dispatcher->getControllerName();
        $action = $this->dispatcher->getActionName();
 
        if ($controller == 'auth'
            && $action != 'session'
            && $action != 'logout'
            && $action != 'setNick'
            && $action != 'registerCompleted'
            && true === $this->auth->isUserSignedIn()) {
            $this->response->redirect('');
 
            return false;
        }
 
        $this->view->section_sex = 0;
        $this->view->section_gay = 0;
        $this->view->section_lesbianas = 0;
 
        if (false === $this->auth->isUserSignedIn()
            && (in_array($controller, $this->private_controllers)
            || (isset($this->private_actions[$controller]) && in_array($action, $this->private_actions[$controller])))) {
            $this->flashSession->warning($this->_t('You have to login to access to this module.'));
            $this->response->redirect('auth/login');
            $this->session->set('web_redirect_after_login', $this->request->getQuery('_url'));
            $dispatcher->forward([
                'controller' => 'auth',
                'action' => 'login',
            ]);
 
            return false;
        }
 
        if ($controller == 'auth') {
            $redir = $this->request->getQuery('redir', 'string', '');
            if ($redir != '') {
                if ($action == 'login') {
                    $this->session->set('web_redirect_after_login', $redir);
                    $this->session->remove('web_redirect_after_register');
                } elseif ($action == 'register') {
                    $this->session->remove('web_redirect_after_login');
                    $this->session->set('web_redirect_after_register', $redir);
                }
            }
        }
 
        /*
         * Getting environment and common template variables
         */
        $this->getEnvironment();
 
        /*
         * Building assets
         */
        if ($controller == 'webchat') {
            $this->buildWebchatAssets();
        } else {
            $this->buildAssets();
        }
 
        if ($this->auth->isUserSignedIn() || $this->auth->checkAuthCookie()) {
            $this->view->loggedIn = true;
            $this->view->identity = $this->auth->syncIdentity();
            $identity = $this->auth->getIdentity();
            $this->view->loggedInUser = $this->getDI()->get('user_manager')->getByEmail($identity['email']);
            $this->view->hasPremium = $this->view->loggedInUser->hasSubscriptionType('premium');
            if ($this->view->hasPremium) {
                $s = $this->view->loggedInUser->getSubscriptionType('premium');
                $this->view->premiumSubscription = $s->getPlan()->getSlug();
            } else {
                $this->view->premiumSubscription = false;
            }
        } else {
            $this->view->loggedIn = false;
            $this->view->hasPremium = false;
        }
 
        $this->getDefaultMetaTags();
 
        $this->view->rating_avg = 4.9;
        $this->view->rating_best = 5;
        $this->view->rating_total = 5982;
 
        $this->view->client_ip = $this->request->getClientAddress();
        $this->view->client_real_ip = $this->request->getClientAddress(true);
 
        try {
            $this->view->gline = $this->getDI()->get('inspircd_gline_manager')->findActiveByIp($this->view->client_real_ip);
        } catch (\Exception $e) {
        }
 
        $this->view->controller = $controller;
        $this->view->action = $action;
        $this->view->backLink = $this->request->getServer('HTTP_REFERER');
        $this->view->baseUri = $this->config->application->baseUri;
        $queryString = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '/';
        $this->view->fullUri = $this->config->application->baseUri.substr($queryString, 1);
        $this->view->supportUri = $this->config->application->supportUri;
        $this->view->locale = $this->translate->getLanguage();
        $this->view->chatmovilUri = $this->config->application->chatmovilUri;
        $this->view->kiwiUri = $this->config->application->kiwiUri;
        $this->view->current_year = date('Y');
 
        $this->view->facebook_app_id = $this->config->facebook->app_id;
 
        /**
         * Random channel datasheets
         */
        $this->view->random_channels = $this->getDI()->get('inspircd_channel_manager')->getRandomDatasheets(3);
 
        // Special content type in utils controller
        if ($this->view->controller == 'utils') {
            if ($this->view->action == 'sitemap') {
                $this->response->setHeader('Content-Type', 'text/xml; charset=utf-8');
            } else {
                $this->response->setHeader('Content-Type', 'text/plain; charset=utf-8');
            }
        } else {
            $this->response->setHeader('Content-Type', 'text/html; charset=utf-8');
        }
 
        $this->view->cdnUri = $this->config->cdn->baseUri;
 
        // Ads config
        $this->view->ads_revive_enabled = false;
        // TODO: Temporary disabling revive ads except few pages
        if ($this->view->controller == 'stats' || ($this->view->controller == 'channel' && $this->view->action == 'stats') || $this->view->controller == 'stories') {
            // Always awin
            $this->view->ads_revive_enabled = true;
            // } else {
            // $rnd = rand(1, 100);
            // 70% bidding, 30% revive
            //$this->view->ads_revive_enabled = ($rnd >= 71 && $rnd <= 100);
        }
        // We add this to make revive work
        if (true === $this->view->ads_revive_enabled) {
            $this->response->setHeader('Access-Control-Allow-Origin', $this->request->getHTTPReferer());
        }
        $this->view->ads_onnetwork_enabled = true;
        $this->view->ads_optimanetwork_enabled = false;
 
        // Refinery89
        // TODO: setup a 50% chance traffic when tests are finished
        $this->view->refinery89_enabled = true;
 
        // Wechat version
        $this->view->enable_element = $this->config->webchat->enable_element;
 
        // Paywall promo
        $promo_enabled = $this->config->paywall->promo->enabled;
        $promo_start_time = $this->config->paywall->promo->start_time;
        $promo_end_time = $this->config->paywall->promo->end_time;
        $this->view->paywall_promo_enabled = $promo_enabled && time() >= strtotime($promo_start_time) && time() <= strtotime($promo_end_time);
        $this->view->paywall_promo_image = $this->config->paywall->promo->image;
        $this->view->paywall_promo_button_link = $this->config->paywall->promo->button_link;
        $this->view->paywall_promo_keyword_header = $this->_t($this->config->paywall->promo->keyword_header);
        $this->view->paywall_promo_keyword_description = $this->_t($this->config->paywall->promo->keyword_description);
    }
 
    public function afterExecuteRoute($dispatcher)
    {
        $this->view->enable_moment = $this->enable_moment;
        $this->view->enable_charts = $this->enable_charts;
        $this->view->enable_editor = $this->enable_editor;
 
        /*
         * Default META tags
         */
        $this->setMetaTags();
    }
 
    public function goToLastPage()
    {
        $this->response->redirect($this->request->getServer('HTTP_REFERER'));
    }
 
    /**
     * Translator.
     *
     * @param string $message
     */
    protected function _t($message, $vars = null)
    {
        return $this->translate->translate($message, $vars, 'web');
    }
 
    protected function buildResponse()
    {
        $response = (new \Phalcon\Http\Response())
            ->setStatusCode(200, 'OK')
            ->setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
 
        return $response;
    }
 
    protected function checkSection($section)
    {
        $sex = 0;
        $gay = 0;
        $lesbianas = 0;
        switch ($section) {
            case 'ADU':
                $sex = 1;
                break;
            case 'LES':
                $lesbianas = 1;
                break;
            case 'GAY':
                $gay = 1;
                break;
            default:
                break;
        }
        $this->view->section_sex = $sex;
        $this->view->section_gay = $gay;
        $this->view->section_lesbianas = $lesbianas;
    }
 
    protected function checkChannelSection(Channel $channel)
    {
        /**
         * Getting section to know if it is a sex/gay/lesb channel or not
         * to put different ad zones (#1471)
         */
        $lesbianas = 0;
        $gay = 0;
        $sex = 0;
        if ($channel) {
            $options = $channel->getOptions();
            if ($options['noads'] == 1 || $options['noads'] == '1') {
                /**
                 * If ads are manually disabled, we mark as adults to avoid showing ads
                 */
                $sex = 1;
            } else {
                try {
                    $section = $channel->getSectionObj();
                    if ($section) {
                        switch ($section->getCode()) {
                            case 'ADU':
                                $sex = 1;
                                break;
                            case 'LES':
                                $lesbianas = 1;
                                break;
                            case 'GAY':
                                $gay = 1;
                                break;
                            default:
                                break;
                        }
                    } else {
                        $debug_channel = $this->config->irc->opers_channel;
                        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
                        $irc_manager->privmsgOrderEnqueue('AAAAAA', $debug_channel, "El canal\x02 ".$channel->getName()."\x02 no tiene sección en la BBDD");
                        $sex = 1;
                    }
                } catch (\Exception $e) {
                    /**
                     * If we catch an error getting section, we avoid ads
                     */
                    $sex = 1;
                }
            }
        } else {
            /**
             * If there is no channel, we avoid ads
             */
            $sex = 1;
        }
        $this->view->section_sex = $sex;
        $this->view->section_gay = $gay;
        $this->view->section_lesbianas = $lesbianas;
    }
 
    protected function buildWebchatAssets()
    {
        $this->assets->collection('webchatCss')
            ->addInlineCss(file_get_contents(__DIR__.'/../../../public/assets/compiled/webchatCss.min.css'));
        $this->assets->collection('webchatJs')
            ->addInlineJs(file_get_contents(__DIR__.'/../../../public/assets/compiled/webchatJs.min.js'));
        $this->assets->collection('wpluginJs')
            ->addInlineJs(file_get_contents(__DIR__.'/../../../public/assets/compiled/wpluginJs.min.js'));
    }
 
    protected function buildAssets()
    {
        $this->assets->collection('headerCss')
            ->addInlineCss(file_get_contents(__DIR__.'/../../../public/assets/compiled/headerCss.min.css'));
        $this->assets->collection('mainJs')
            ->addInlineJs(file_get_contents(__DIR__.'/../../../public/assets/compiled/mainJs.min.js'));
        $this->assets->collection('pluginJs')
            ->addInlineJs(file_get_contents(__DIR__.'/../../../public/assets/compiled/pluginJs.min.js'));
 
        if ($this->enable_moment === true) {
            $this->assets->collection('momentJs')
                ->addInlineJs(file_get_contents(__DIR__.'/../../../public/assets/compiled/momentJs.min.js'));
        }
 
        if ($this->enable_charts === true) {
            $this->assets->collection('chartJs')
                ->addInlineJs(file_get_contents(__DIR__.'/../../../public/assets/compiled/chartJs.min.js'));
        }
 
        if ($this->enable_editor === true) {
            $baseUri = $this->config->application->baseUri;
            $this->assets->collection('ckeditorJs')
                ->addJs('assets/web/plugins/ckeditor/ckeditor.js')
                ->addJs('assets/web/plugins/ckeditor/lang/en.js')
                ->addJs('assets/web/plugins/ckeditor/lang/es.js')
                ->addJs('assets/web/plugins/ckeditor/lang/ca.js')
                ->addJs('assets/web/js/ckeditor/youtube/plugin.js')
                ->addJs('assets/web/js/ckeditor/youtube/lang/en.js')
                ->addJs('assets/web/js/ckeditor/youtube/lang/es.js');
        }
    }
}
#15ChatHispanoEngine\Web\Controllers\BaseController->beforeExecuteRoute
/srv/ChatHispanoEngine/releases/94/apps/Web/Controllers/ChannelController.php (16)
<?php
 
namespace ChatHispanoEngine\Web\Controllers;
 
use ChatHispanoEngine\Core\Library\Cdn\DefaultCdn as Cdn;
 
class ChannelController extends BaseController
{
    public function beforeExecuteRoute($dispatcher)
    {
        $action = $this->dispatcher->getActionName();
        if ($action == 'stats') {
            $this->enable_moment = true;
            $this->enable_charts = true;
        }
        parent::beforeExecuteRoute($dispatcher);
    }
 
    public function indexAction()
    {
        $this->response->redirect('');
    }
 
    /**
     * Channel datasheet.
     */
    public function infoAction($channel)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
 
        try {
            $this->view->channel = $channel_manager->get('#'.$channel, true);
            $options = $this->view->channel->getOptions();
            $this->view->forumBaseUri = $this->getDI()->get('config')->discourse->baseUri;
            $this->view->forumEnabled = $options && is_array($options) && isset($options['forum']) ? $options['forum'] : false;
            $s = $this->view->channel->getSectionObj();
            $this->view->section = $s;
            $channel_manager->checkAccess('#'.$channel, $this->auth->getIdentity());
 
            $this->checkChannelSection($this->view->channel);
 
            $this->setHeadTitle('Ficha canal #'.$channel);
            $this->setMetaDescription('Ficha del canal '.$channel.' de la red de IRC ChatHispano');
 
            $datasheet = $this->view->channel->getDatasheet();
            if ($datasheet && $datasheet->getCanonical() == 1) {
                $this->setRelCanonical($this->config->application->baseUri.'canal/'.$channel);
            }
            $this->view->override = $this->view->channel->getOverride();
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
            return;
        }
 
        // Navigation
        if ($s) {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('%name% theme', ['name' => $s->getName()]), 'url' => '/chats-'.$s->getSlug(), ],
                ['name' => $this->_t('Chat %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
            ];
        } else {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('Chat %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
            ];
        }
    }
 
    /**
     * Channel stats
     */
    public function statsAction($channel)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            $this->view->channel = $channel_manager->get('#'.$channel, true);
            $s = $this->view->channel->getSectionObj();
 
            $this->checkChannelSection($this->view->channel);
 
            $this->setHeadTitle('Estadísticas del canal #'.$channel);
            $this->setMetaDescription('Estadísticas del canal '.$channel.' de la red de IRC ChatHispano');
 
            $datasheet = $this->view->channel->getDatasheet();
            if ($datasheet && $datasheet->getCanonical() == 1) {
                $this->setRelCanonical($this->config->application->baseUri.'canal/'.$channel.'/stats');
            }
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
            return;
        }
 
        if ($this->view->channel->getIg() != 1) {
            $this->flashSession->error($this->_t("Stats are not enabled for this channel."));
            $this->response->redirect('/canal/'.$channel);
            return;
        }
 
        try {
            $date = $this->request->getQuery('date', 'string', date('Y-m-d'));
            if ($date == '') {
                $date = date('Y-m-d');
            }
            $this->view->today = date('Y-m-d');
            $this->view->showReset = ($date != date('Y-m-d'));
            $date_end = date('Y-m-d', strtotime($date.' +1 day'));
            $this->view->date = $date;
            $stats_manager = $this->getDI()->get('stats_manager');
            $this->view->stats_data = $stats_manager->getChannelBetweenDates('#'.$channel, $date, $date_end);
            $cnt = 0;
            $sum = 0;
            $min = null;
            $max = null;
            foreach ($this->view->stats_data as $sd) {
                $cnt++;
                $sum += $sd->users;
                if ($min === null || $sd->users < $min) {
                    $min = $sd->users;
                    $min_time = date('H:i', $sd->date->toDateTime()->getTimestamp());
                }
                if ($max === null || $sd->users > $max) {
                    $max = $sd->users;
                    $max_time = date('H:i', $sd->date->toDateTime()->getTimestamp());
                }
            }
            $this->view->cnt = $cnt;
            $this->view->min = $min;
            $this->view->min_time = $min_time;
            $this->view->max = $max;
            $this->view->max_time = $max_time;
            $this->view->average = round($sum / $cnt, 1);
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
            return;
        }
 
        // Navigation
        if ($s) {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('%name% theme', ['name' => $s->getName()]), 'url' => '/chats-'.$s->getSlug(), ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Stats', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/stats', ],
            ];
        } else {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Stats', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/stats', ],
            ];
        }
    }
 
    /**
     * Channel blog.
     */
    public function blogAction($channel)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            $editor = $identity && isset($identity['blogs_data']) && isset($identity['blogs_data'][$channel]) ? true : false;
            $this->view->channel = $channel_manager->get('#'.$channel, true);
            $s = $this->view->channel->getSectionObj();
            $this->view->editor = $editor;
 
            $this->checkChannelSection($this->view->channel);
 
            $page = $this->request->getQuery('page', 'int', 1);
            $this->view->page = $page;
            $this->view->posts = $blog_manager->getPostsPaginated('#'.$channel, $page, $editor);
            $this->view->total_posts = $blog_manager->countPosts('#'.$channel, $editor);
            if ($editor === false && $this->view->total_posts < 1) {
                $this->flashSession->notice($this->_t('Sorry, there are no posts yet.'));
                $this->response->redirect('canal/'.$channel);
                return;
            }
            $this->view->pages = ceil($this->view->total_posts / 10);
 
            /*
             * Setting NOINDEX & NOFOLLOW META tags if there are no blog posts.
             */
            if ($this->view->total_posts < 1) {
                $this->setMetaNoindex(true);
                $this->setMetaNofollow(true);
            }
 
            $this->setHeadTitle('Blog canal #'.$channel);
            $this->setMetaDescription('Blog del canal '.$channel.' de la red de IRC ChatHispano');
 
            $datasheet = $this->view->channel->getDatasheet();
            if ($datasheet && $datasheet->getCanonical() == 1) {
                $this->setRelCanonical($this->config->application->baseUri.'canal/'.$channel.'/blog');
            }
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
            return;
        }
 
        // Navigation
        if ($s) {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('%name% theme', ['name' => $s->getName()]), 'url' => '/chats-'.$s->getSlug(), ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
            ];
        } else {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
            ];
        }
    }
 
    public function viewAction($channel, $slug)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            if ($identity && isset($identity['blogs_data']) && isset($identity['blogs_data'][$channel])) {
                $editor = true;
                $o = $blog_manager->getPostBySlug('#'.$channel, $slug, $editor);
                $o = $blog_manager->viewPost($o, $identity['email']);
            } else {
                $editor = false;
                $o = $blog_manager->getPostBySlug('#'.$channel, $slug, $editor);
                $ip = $this->request->getClientAddress(true);
                $o = $blog_manager->viewPost($o, $ip);
            }
            $this->setHeadTitle($o->getTitle().' - Blog canal #'.$channel);
            if ($o->getDescription() && $o->getDescription() != '') {
                $this->setMetaDescription($o->getDescription());
            }
            if ($o->getImage() && $o->getImage() != '') {
                $this->setMetaImage($o->getImage());
            }
            $this->setArticleMeta(explode(',', $o->getTags()), 'blog', $o->getCreatedAt(), $o->getUpdatedAt());
            if ($o->getNoindex() == 1) {
                $this->setMetaNoindex(true);
            }
            $this->view->channel = $channel_manager->get('#'.$channel, true);
            $s = $this->view->channel->getSectionObj();
            $this->view->post = $o;
            $this->view->editor = $editor;
 
            $this->checkChannelSection($this->view->channel);
 
            $datasheet = $this->view->channel->getDatasheet();
            if ($datasheet && $datasheet->getCanonical() == 1) {
                $this->setRelCanonical($this->config->application->baseUri.'canal/'.$channel.'/blog/'.$slug);
            }
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('canal/'.$channel.'/blog');
            return;
        }
 
        // Navigation
        if ($s) {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('%name% theme', ['name' => $s->getName()]), 'url' => '/chats-'.$s->getSlug(), ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
                ['name' => $o->getTitle(), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/'.$slug, ],
            ];
        } else {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
                ['name' => $o->getTitle(), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/'.$slug, ],
            ];
        }
    }
 
    public function reportAction($channel, $slug)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
 
        try {
            $channel = $channel_manager->get('#'.$channel, true);
 
            $this->checkChannelSection($channel);
 
            if ($this->request->isPost()) {
                try {
                    $identity = $this->getDI()->get('auth')->getIdentity();
                    if ($identity) {
                        $item = $identity['email'];
                    } else {
                        $item = $this->request->getClientAddress(true);
                    }
                    $blog_manager->reportPost('#'.$channel, $slug, $item, $this->request->getPost('reason', 'string', ''));
                } catch (\Exception $e) {
                    $this->flashSession->error($this->_t($e->getMessage()));
                }
            }
 
            $this->setHeadTitle('Reportar post del blog - Canal '.$channel);
            $this->setMetaDescription('Reportar un post del blog del canal #'.$channel.' de la red de IRC ChatHispano');
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
            return;
        }
 
        $this->response->redirect('canal/'.$channel->getNamePretty().'/blog/'.$slug);
    }
 
    public function postAction($channel)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
        $user_manager = $this->getDI()->get('user_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            try {
                $user = $user_manager->getByEmail($identity['email']);
                if ($user->getBlogConditionsAccepted() < 1) {
                    $this->flashSession->notice($this->_t('Please accept the conditions first.'));
 
                    $this->response->redirect('condiciones/');
                    return;
                }
            } catch (\Exception $e) {
                $this->flashSession->error($this->_t('You need to be logged in.'));
 
                $this->response->redirect('canal/'.$channel.'/blog');
                return;
            }
 
            $this->view->channel = $channel_manager->get('#'.$channel, true);
            $s = $this->view->channel->getSectionObj();
 
            $this->checkChannelSection($this->view->channel);
 
            if (!$identity || !isset($identity['blogs_data']) || !isset($identity['blogs_data'][$channel])) {
                $this->flashSession->error($this->_t('Sorry, you\'re not allowed to post.'));
 
                $this->response->redirect('canal/'.$channel.'/blog');
                return;
            }
 
            if ($this->request->isPost()) {
                try {
                    $blog_manager->createPost($channel, $this->request->getPost('nick', 'string', ''), $this->request->getPost());
 
                    $this->response->redirect('canal/'.$channel.'/blog');
                    return;
                } catch (\Exception $e) {
                    $this->flashSession->error($e->getMessage());
                }
            }
 
            $datasheet = $this->view->channel->getDatasheet();
            if ($datasheet && $datasheet->getCanonical() == 1) {
                $this->setRelCanonical($this->config->application->baseUri.'canal/'.$channel.'/post');
            }
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
        }
 
        // Navigation
        if ($s) {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('%name% theme', ['name' => $s->getName()]), 'url' => '/chats-'.$s->getSlug(), ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
                ['name' => $this->_t('Create a post', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/post', ],
            ];
        } else {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
                ['name' => $this->_t('Create a post', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/post', ],
            ];
        }
    }
 
    public function editAction($channel, $slug)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
        $user_manager = $this->getDI()->get('user_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            try {
                $user = $user_manager->getByEmail($identity['email']);
                if ($user->getBlogConditionsAccepted() < 1) {
                    $this->flashSession->notice($this->_t('Please accept the conditions first.'));
 
                    $this->response->redirect('condiciones/');
                    return;
                }
            } catch (\Exception $e) {
                $this->flashSession->error($this->_t('You need to be logged in.'));
 
                $this->response->redirect('canal/'.$channel.'/blog');
                return;
            }
 
            if (!$identity || !isset($identity['blogs_data']) || !isset($identity['blogs_data'][$channel])) {
                $this->flashSession->error($this->_t('Sorry, you\'re not allowed to post.'));
 
                $this->response->redirect('canal/'.$channel.'/blog');
                return;
            }
 
            $o = $blog_manager->getPostBySlug('#'.$channel, $slug, true);
            $this->view->post = $o;
 
            if ($this->request->isPost()) {
                try {
                    $blog_manager->updatePost($o, $this->request->getPost('nick', 'string', ''), $this->request->getPost());
                    $this->flashSession->success($this->_t('Blog post saved successfully.'));
 
                    $this->response->redirect('canal/'.$channel.'/blog/'.$o->getSlug());
                    return;
                } catch (\Exception $e) {
                    $this->flashSession->error($e->getMessage());
                }
            }
 
            $this->view->channel = $channel_manager->get('#'.$channel, true);
            $s = $this->view->channel->getSectionObj();
 
            $this->checkChannelSection($this->view->channel);
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
 
            $this->response->redirect('');
        }
 
        // Navigation
        if ($s) {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('%name% theme', ['name' => $s->getName()]), 'url' => '/chats-'.$s->getSlug(), ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
                ['name' => $o->getTitle(), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/'.$slug, ],
                ['name' => $this->_t('Edit', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/'.$slug.'/edit', ],
            ];
        } else {
            $this->view->navigation = [
                ['name' => $this->_t('Home', []), 'url' => '/', ],
                ['name' => $this->_t('Channel %name%', ['name' => $this->view->channel->getNamePretty()]), 'url' => '/canal/'.$this->view->channel->getNamePretty(), ],
                ['name' => $this->_t('Blog', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog', ],
                ['name' => $o->getTitle(), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/'.$slug, ],
                ['name' => $this->_t('Edit', []), 'url' => '/canal/'.$this->view->channel->getNamePretty().'/blog/'.$slug.'/edit', ],
            ];
        }
    }
 
    public function publishAction($channel, $slug)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
        $user_manager = $this->getDI()->get('user_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            try {
                $user = $user_manager->getByEmail($identity['email']);
                if ($user->getBlogConditionsAccepted() < 1) {
                    $this->flashSession->notice($this->_t('Please accept the conditions first.'));
 
                    $this->response->redirect('condiciones/');
                    return;
                }
            } catch (\Exception $e) {
                $this->flashSession->error($this->_t('You need to be logged in.'));
 
                $this->response->redirect('canal/'.$channel.'/blog');
                return;
            }
 
            if (!$identity || !isset($identity['blogs_data']) || !isset($identity['blogs_data'][$channel])) {
                $this->flashSession->error($this->_t('Sorry, you\'re not allowed to post.'));
            } else {
                try {
                    $blog_manager->publishPostBySlug('#'.$channel, $slug);
                    $this->flashSession->success($this->_t('Blog post published successfully.'));
                } catch (\Exception $e) {
                    $this->flashSession->error($e->getMessage());
                }
            }
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t('Channel %name% does not exist.'));
        }
 
        $this->response->redirect($this->view->backLink);
    }
 
    public function unpublishAction($channel, $slug)
    {
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $blog_manager = $this->getDI()->get('blog_manager');
        $user_manager = $this->getDI()->get('user_manager');
 
        try {
            $identity = $this->getDI()->get('auth')->getIdentity();
            try {
                $user = $user_manager->getByEmail($identity['email']);
                if ($user->getBlogConditionsAccepted() < 1) {
                    $this->flashSession->notice($this->_t('Please accept the conditions first.'));
 
                    $this->response->redirect('condiciones/');
                    return;
                }
            } catch (\Exception $e) {
                $this->flashSession->error($this->_t('You need to be logged in.'));
 
                $this->response->redirect('canal/'.$channel.'/blog');
                return;
            }
 
            if (!$identity || !isset($identity['blogs_data']) || !isset($identity['blogs_data'][$channel])) {
                $this->flashSession->error($this->_t('Sorry, you\'re not allowed to post.'));
            } else {
                try {
                    $blog_manager->unpublishPostBySlug('#'.$channel, $slug);
                    $this->flashSession->success($this->_t('Blog post unpublished successfully.'));
                } catch (\Exception $e) {
                    $this->flashSession->error($this->_t($e->getMessage()));
                }
            }
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
        }
 
        $this->response->redirect($this->view->backLink);
    }
 
    public function likeAction($channel, $slug)
    {
        $this->view->disable();
 
        try {
            $blog_manager = $this->getDI()->get('blog_manager');
            $identity = $this->getDI()->get('auth')->getIdentity();
            $o = $blog_manager->likePost('#'.$channel, $slug, $identity['email']);
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
        }
 
        $this->response->redirect($this->view->backLink);
    }
 
    public function dislikeAction($channel, $slug)
    {
        $this->view->disable();
 
        try {
            $blog_manager = $this->getDI()->get('blog_manager');
            $identity = $this->getDI()->get('auth')->getIdentity();
            $o = $blog_manager->dislikePost('#'.$channel, $slug, $identity['email']);
        } catch (\Exception $e) {
            $this->flashSession->error($this->_t($e->getMessage()));
        }
 
        $this->response->redirect($this->view->backLink);
    }
 
    public function browseAction($channel)
    {
        $this->view->func = $this->request->getQuery('CKEditorFuncNum', 'string', 'func');
 
        $path = $this->config->cdn->path;
        $this->view->baseUri = $this->config->cdn->baseUri;
        if (!file_exists($path.'/channels/'.$channel.'/')) {
            mkdir($path.'/channels/'.$channel.'/');
        }
        $channel_manager = $this->getDI()->get('inspircd_channel_manager');
        $o = $channel_manager->get('#'.$channel, true);
        $this->view->channel = $channel;
        $this->view->files = scandir($path.'/channels/'.$channel.'/');
 
        $this->checkChannelSection($o);
    }
 
    public function uploadAction($channel)
    {
        $this->view->disable();
 
        $uploaded_files = $this->request->getUploadedFiles();
 
        if (count($uploaded_files) < 1) {
            return $this->renderOutput('No file selected', 500);
        }
 
        $path = $this->config->cdn->path;
 
        if (!file_exists($path.'/channels/'.$channel.'/')) {
            if (false === mkdir($path.'/channels/'.$channel.'/')) {
                return $this->renderOutput('Missing permissions', 500);
            }
        }
 
        foreach ($uploaded_files as $file) {
            if (false === $file->moveTo($path.'/channels/'.$channel.'/'.$file->getName())) {
                return $this->renderOutput('Missing permissions', 500);
            }
        }
 
        // Cdn
        if ($this->env == 'prod') {
            $cdn_config = $this->config->cdn->toArray();
            foreach ($cdn_config['nodes'] as $node) {
                if ($this->request->getServerAddress() != $node['host']) {
                    try {
                        $cdn = new Cdn($node['host'], $node['user'], $node['passwd'], 'channels/'.$channel);
                        $cdn->connect();
                        $cdn->upload([
                            $path.'/channels/'.$channel.'/'.$file->getName() => $file->getName(),
                        ]);
                    } catch (\Exception $e) {
                        $this->getDI()->get('redis_cluster')->rPush('chathispano_ftp_pending', json_encode([
                            'server' => $node['host'],
                            'user' => $node['user'],
                            'passwd' => $node['passwd'],
                            'path' => $path.'/channels/'.$channel.'/'.$file->getName(),
                            'folder' => 'channels/'.$channel,
                            'name' => $file->getName(),
                        ]));
                    }
                }
            }
        }
 
        return $this->renderOutput([
            "uploaded" => 1,
            "fileName" => $file->getName(),
            "url" => $this->config->cdn->baseUri.'/channels/'.$channel.'/'.$file->getName(),
        ]);
    }
 
    public function searchAction($query)
    {
        $query = str_replace('#', '', $query);
 
        $this->view->disable();
 
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
 
        $list = [];
 
        try {
            $list = $irc_manager->getChannelsByNicks($query);
        } catch (\Exception $e) {
        }
 
        return $this->renderOutput($list);
    }
 
    public function searchBlogsAction($query)
    {
        $this->view->disable();
 
        $blog_manager = $this->getDI()->get('blog_manager');
        $posts = $blog_manager->search($query);
        $list = [];
        foreach ($posts as $o) {
            $list[] = $o->toArray();
        }
 
        return $this->renderOutput($list);
    }
}
#16ChatHispanoEngine\Web\Controllers\ChannelController->beforeExecuteRoute
#17Phalcon\Dispatcher\AbstractDispatcher->dispatch
#18Phalcon\Mvc\Application->handle
/srv/ChatHispanoEngine/releases/94/apps/Application.php (150)
<?php
 
// namespace ChatHispanoEngine;
 
/**
 * Application driver class to initialize Phalcon and
 * other resources.
 */
class Application extends \Phalcon\Mvc\Application
{
    private static $mode = 'dev';
 
    private static $DEFAULT_MODULE = 'api';
 
    public const MODE_PRODUCTION = 'prod';
    public const MODE_STAGING = 'staging';
    public const MODE_TEST = 'test';
    public const MODE_DEVELOPMENT = 'dev';
 
    /**
     * Set application mode and error reporting level.
     */
    public function __construct($defaultModule, $env = 'dev')
    {
        $this->modules = array(
            'core' => array(
                'className' => 'ChatHispanoEngine\Core\Module',
                'path' => __DIR__.'/Core/Module.php',
            ),
            'api' => array(
                'className' => 'ChatHispanoEngine\Api\Module',
                'path' => __DIR__.'/Api/Module.php',
            ),
            'login' => array(
                'className' => 'ChatHispanoEngine\Login\Module',
                'path' => __DIR__.'/Login/Module.php',
            ),
            'oidc' => array(
                'className' => 'ChatHispanoEngine\Oidc\Module',
                'path' => __DIR__.'/Oidc/Module.php',
            ),
            'web' => array(
                'className' => 'ChatHispanoEngine\Web\Module',
                'path' => __DIR__.'/Web/Module.php',
            ),
            'backoffice' => array(
                'className' => 'ChatHispanoEngine\Backoffice\Module',
                'path' => __DIR__.'/Backoffice/Module.php',
            ),
            'movil' => array(
                'className' => 'ChatHispanoEngine\Movil\Module',
                'path' => __DIR__.'/Movil/Module.php',
            ),
            'regwebexternal' => array(
                'className' => 'ChatHispanoEngine\RegWebExternal\Module',
                'path' => __DIR__.'/Regwebexternal/Module.php',
            ),
            'cdn' => array(
                'className' => 'ChatHispanoEngine\Cdn\Module',
                'path' => __DIR__.'/Cdn/Module.php',
            ),
            'shorten' => array(
                'className' => 'ChatHispanoEngine\Shorten\Module',
                'path' => __DIR__.'/Shorten/Module.php',
            ),
        );
 
        static::$DEFAULT_MODULE = $defaultModule;
        self::$mode = $env;
 
        self::$mode = trim(file_get_contents(__DIR__.'/../config/environment.txt'));
        define('ENVIRONMENT', self::$mode);
 
        if (!defined('PHALCON_MODE')) {
            $mode = getenv('PHALCON_MODE');
            $mode = $mode ? $mode : self::$mode;
            define('PHALCON_MODE', $mode);
        }
 
        switch (self::getMode()) {
            case self::MODE_PRODUCTION:
            case self::MODE_STAGING:
                error_reporting(0);
                break;
            case self::MODE_TEST:
            case self::MODE_DEVELOPMENT:
                ini_set('display_errors', 'On');
                error_reporting(E_ALL);
                break;
        }
    }
 
    /**
     * Register the services here to make them general or register in
     * the ModuleDefinition to make them module-specific.
     */
    protected function _registerServices()
    {
        $defaultModule = self::$DEFAULT_MODULE;
        $modules = $this->modules;
        $config = include __DIR__.'/../config/config.php';
        $env_config = include __DIR__.'/../config/config_'.ENVIRONMENT.'.php';
        $config->merge($env_config);
 
        $di = new \Phalcon\DI\FactoryDefault();
 
        include __DIR__.'/../config/loader.php';
        include __DIR__.'/../config/services.php';
        include __DIR__.'/../config/routing.php';
 
        $this->setDI($di);
    }
 
    /**
     * Run the application.
     */
    public function main()
    {
        if (static::MODE_PRODUCTION === static::getMode()) {
            $this->mainProd();
        } else {
            $this->mainDev();
        }
    }
 
    private function getRequestUri()
    {
        if (!isset($_SERVER)) {
            return "/";
        }
        if (!is_array($_SERVER)) {
            return "/";
        }
        if (!isset($_SERVER['REQUEST_URI'])) {
            return "/";
        }
        return $_SERVER['REQUEST_URI'];
    }
 
    /**
     * Run the development environment.
     */
    private function mainDev()
    {
        (new \Phalcon\Support\Debug())->listen();
 
        $this->_registerServices();
        $this->registerModules($this->modules);
 
        $response = $this->handle($this->getRequestUri());
        $response->send();
    }
 
    /**
     * Run the production environment.
     */
    private function mainProd()
    {
        try {
            $this->registerModules($this->modules);
            $this->_registerServices();
 
            $response = $this->handle($this->getRequestUri());
            $response->send();
        } catch (\Exception $e) {
            $logger = new \Phalcon\Logger\Adapter\Stream(__DIR__.'/../logs/'.date('Y-m-d').'.log');
            $msg = "[".$_SERVER['SERVER_NAME']."] [".$_SERVER['REQUEST_URI']."] [".$e->getCode()."] ".$e->getMessage()." at ".$e->getFile()." (".$e->getLine().")";
            $msg .= "\n".$e->getTraceAsString();
            $logger->process(new \Phalcon\Logger\Item($msg, "error", 100));
            $logger->close();
 
            // remove view contents from buffer
            ob_clean();
 
            $errorCode = 500;
            $errorView = __DIR__.'/../public/errors/error.html';
 
            if (401 === $e->getCode()) {
                // 401 UNAUTHORIZED
                $errorCode = 401;
            } elseif (403 === $e->getCode()) {
                // 403 FORBIDDEN
                $errorCode = 403;
            } elseif (404 === $e->getCode()
                || $e instanceof Phalcon\Mvc\View\Exception
                || $e instanceof Phalcon\Mvc\Dispatcher\Exception) {
                // 404 NOT FOUND
                $errorCode = 404;
            }
 
            // Get error view contents. Since we are including the view
            // file here you can use PHP and local vars inside the error view.
            ob_start();
            include_once $errorView;
            $contents = ob_get_contents();
            ob_end_clean();
 
            // send view to header
            $response = $this->getDI()->getShared('response');
            $response->resetHeaders()
                ->setStatusCode($errorCode, null)
                ->setContent($contents)
                ->send()
            ;
 
            /**
             * We try to register in MongoDB the error to be able to
             * track it in backoffice and/or receive emails
             */
            try {
                $system_log_manager = $this->getDI()->get('system_log_manager');
                if ($errorCode == 500) {
                    $system_log_manager->createError([
                        'ip' => $_SERVER['SERVER_ADDR'],
                        'host' => $_SERVER['SERVER_NAME'],
                        'process' => 'php-fpm',
                        'message' => $e->getMessage(),
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                    ]);
                } else {
                    $system_log_manager->createWarning([
                        'ip' => $_SERVER['SERVER_ADDR'],
                        'host' => $_SERVER['SERVER_NAME'],
                        'process' => 'php-fpm',
                        'message' => $e->getMessage(),
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                    ]);
                }
            } catch (\Exception $e) {
            }
        }
    }
 
    public function slowLog($t)
    {
        $config = $this->getDI()->get('config');
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $server = gethostname() ? gethostname() : 'unknown';
        $uri = "https://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
        $msg = "\x02[SLOWLOG] [".str_pad($server, 6, " ", STR_PAD_LEFT)."] [".str_pad(round($t, 1), 5, " ", STR_PAD_LEFT)."s] PATH=\x02 ".$_SERVER['REQUEST_URI']
            ."\x02 TYPE=\x02 ".$_SERVER['REQUEST_METHOD']."\x02 URI=\x02 ".$uri;
        $irc_manager->privmsgOrderEnqueue('AAAAAI', $config->irc->debug_channel, $msg);
    }
 
    /**
     * Get the current mode.
     *
     * @return string
     */
    public static function getMode()
    {
        return self::$mode;
    }
}
#19Application->mainDev
/srv/ChatHispanoEngine/releases/94/apps/Application.php (122)
<?php
 
// namespace ChatHispanoEngine;
 
/**
 * Application driver class to initialize Phalcon and
 * other resources.
 */
class Application extends \Phalcon\Mvc\Application
{
    private static $mode = 'dev';
 
    private static $DEFAULT_MODULE = 'api';
 
    public const MODE_PRODUCTION = 'prod';
    public const MODE_STAGING = 'staging';
    public const MODE_TEST = 'test';
    public const MODE_DEVELOPMENT = 'dev';
 
    /**
     * Set application mode and error reporting level.
     */
    public function __construct($defaultModule, $env = 'dev')
    {
        $this->modules = array(
            'core' => array(
                'className' => 'ChatHispanoEngine\Core\Module',
                'path' => __DIR__.'/Core/Module.php',
            ),
            'api' => array(
                'className' => 'ChatHispanoEngine\Api\Module',
                'path' => __DIR__.'/Api/Module.php',
            ),
            'login' => array(
                'className' => 'ChatHispanoEngine\Login\Module',
                'path' => __DIR__.'/Login/Module.php',
            ),
            'oidc' => array(
                'className' => 'ChatHispanoEngine\Oidc\Module',
                'path' => __DIR__.'/Oidc/Module.php',
            ),
            'web' => array(
                'className' => 'ChatHispanoEngine\Web\Module',
                'path' => __DIR__.'/Web/Module.php',
            ),
            'backoffice' => array(
                'className' => 'ChatHispanoEngine\Backoffice\Module',
                'path' => __DIR__.'/Backoffice/Module.php',
            ),
            'movil' => array(
                'className' => 'ChatHispanoEngine\Movil\Module',
                'path' => __DIR__.'/Movil/Module.php',
            ),
            'regwebexternal' => array(
                'className' => 'ChatHispanoEngine\RegWebExternal\Module',
                'path' => __DIR__.'/Regwebexternal/Module.php',
            ),
            'cdn' => array(
                'className' => 'ChatHispanoEngine\Cdn\Module',
                'path' => __DIR__.'/Cdn/Module.php',
            ),
            'shorten' => array(
                'className' => 'ChatHispanoEngine\Shorten\Module',
                'path' => __DIR__.'/Shorten/Module.php',
            ),
        );
 
        static::$DEFAULT_MODULE = $defaultModule;
        self::$mode = $env;
 
        self::$mode = trim(file_get_contents(__DIR__.'/../config/environment.txt'));
        define('ENVIRONMENT', self::$mode);
 
        if (!defined('PHALCON_MODE')) {
            $mode = getenv('PHALCON_MODE');
            $mode = $mode ? $mode : self::$mode;
            define('PHALCON_MODE', $mode);
        }
 
        switch (self::getMode()) {
            case self::MODE_PRODUCTION:
            case self::MODE_STAGING:
                error_reporting(0);
                break;
            case self::MODE_TEST:
            case self::MODE_DEVELOPMENT:
                ini_set('display_errors', 'On');
                error_reporting(E_ALL);
                break;
        }
    }
 
    /**
     * Register the services here to make them general or register in
     * the ModuleDefinition to make them module-specific.
     */
    protected function _registerServices()
    {
        $defaultModule = self::$DEFAULT_MODULE;
        $modules = $this->modules;
        $config = include __DIR__.'/../config/config.php';
        $env_config = include __DIR__.'/../config/config_'.ENVIRONMENT.'.php';
        $config->merge($env_config);
 
        $di = new \Phalcon\DI\FactoryDefault();
 
        include __DIR__.'/../config/loader.php';
        include __DIR__.'/../config/services.php';
        include __DIR__.'/../config/routing.php';
 
        $this->setDI($di);
    }
 
    /**
     * Run the application.
     */
    public function main()
    {
        if (static::MODE_PRODUCTION === static::getMode()) {
            $this->mainProd();
        } else {
            $this->mainDev();
        }
    }
 
    private function getRequestUri()
    {
        if (!isset($_SERVER)) {
            return "/";
        }
        if (!is_array($_SERVER)) {
            return "/";
        }
        if (!isset($_SERVER['REQUEST_URI'])) {
            return "/";
        }
        return $_SERVER['REQUEST_URI'];
    }
 
    /**
     * Run the development environment.
     */
    private function mainDev()
    {
        (new \Phalcon\Support\Debug())->listen();
 
        $this->_registerServices();
        $this->registerModules($this->modules);
 
        $response = $this->handle($this->getRequestUri());
        $response->send();
    }
 
    /**
     * Run the production environment.
     */
    private function mainProd()
    {
        try {
            $this->registerModules($this->modules);
            $this->_registerServices();
 
            $response = $this->handle($this->getRequestUri());
            $response->send();
        } catch (\Exception $e) {
            $logger = new \Phalcon\Logger\Adapter\Stream(__DIR__.'/../logs/'.date('Y-m-d').'.log');
            $msg = "[".$_SERVER['SERVER_NAME']."] [".$_SERVER['REQUEST_URI']."] [".$e->getCode()."] ".$e->getMessage()." at ".$e->getFile()." (".$e->getLine().")";
            $msg .= "\n".$e->getTraceAsString();
            $logger->process(new \Phalcon\Logger\Item($msg, "error", 100));
            $logger->close();
 
            // remove view contents from buffer
            ob_clean();
 
            $errorCode = 500;
            $errorView = __DIR__.'/../public/errors/error.html';
 
            if (401 === $e->getCode()) {
                // 401 UNAUTHORIZED
                $errorCode = 401;
            } elseif (403 === $e->getCode()) {
                // 403 FORBIDDEN
                $errorCode = 403;
            } elseif (404 === $e->getCode()
                || $e instanceof Phalcon\Mvc\View\Exception
                || $e instanceof Phalcon\Mvc\Dispatcher\Exception) {
                // 404 NOT FOUND
                $errorCode = 404;
            }
 
            // Get error view contents. Since we are including the view
            // file here you can use PHP and local vars inside the error view.
            ob_start();
            include_once $errorView;
            $contents = ob_get_contents();
            ob_end_clean();
 
            // send view to header
            $response = $this->getDI()->getShared('response');
            $response->resetHeaders()
                ->setStatusCode($errorCode, null)
                ->setContent($contents)
                ->send()
            ;
 
            /**
             * We try to register in MongoDB the error to be able to
             * track it in backoffice and/or receive emails
             */
            try {
                $system_log_manager = $this->getDI()->get('system_log_manager');
                if ($errorCode == 500) {
                    $system_log_manager->createError([
                        'ip' => $_SERVER['SERVER_ADDR'],
                        'host' => $_SERVER['SERVER_NAME'],
                        'process' => 'php-fpm',
                        'message' => $e->getMessage(),
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                    ]);
                } else {
                    $system_log_manager->createWarning([
                        'ip' => $_SERVER['SERVER_ADDR'],
                        'host' => $_SERVER['SERVER_NAME'],
                        'process' => 'php-fpm',
                        'message' => $e->getMessage(),
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                    ]);
                }
            } catch (\Exception $e) {
            }
        }
    }
 
    public function slowLog($t)
    {
        $config = $this->getDI()->get('config');
        $irc_manager = $this->getDI()->get('inspircd_irc_manager');
        $server = gethostname() ? gethostname() : 'unknown';
        $uri = "https://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
        $msg = "\x02[SLOWLOG] [".str_pad($server, 6, " ", STR_PAD_LEFT)."] [".str_pad(round($t, 1), 5, " ", STR_PAD_LEFT)."s] PATH=\x02 ".$_SERVER['REQUEST_URI']
            ."\x02 TYPE=\x02 ".$_SERVER['REQUEST_METHOD']."\x02 URI=\x02 ".$uri;
        $irc_manager->privmsgOrderEnqueue('AAAAAI', $config->irc->debug_channel, $msg);
    }
 
    /**
     * Get the current mode.
     *
     * @return string
     */
    public static function getMode()
    {
        return self::$mode;
    }
}
#20Application->main
/srv/ChatHispanoEngine/releases/94/public/index.php (13)
<?php
 
date_default_timezone_set('Europe/Madrid');
 
require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/../apps/Application.php';
 
if (isset($_SERVER['PHALCON_APP'])) {
    $app = new Application($_SERVER['PHALCON_APP']);
} else {
    $app = new Application('web');
}
$app->main();
 
try {
    $t = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
    if ($t > 6) {
        $app->slowLog($t);
    }
} catch (\Exception $e) {
}
KeyValue
_url/canal/sincensura/blog
KeyValue
USERwww-data
HOME/var/www
HTTP_CONNECTIONclose
HTTP_X_FORWARDED_FOR18.227.0.249
HTTP_X_FORWARDED_PROTOhttp
HTTP_COOKIERWSESSID=8d9247cf81c607e09a47bd57ae01a7f2; fikker-KEPi-Z7Xa=BsBSbwRLNOhAxs6UKNM9KWTshGBARUhu; fikker-2ITi-jkXI=4enQXwhGlJjmPkfWL9j3di8QM5h9hgJJ
HTTP_ACCEPT_ENCODINGgzip, br, zstd, deflate
HTTP_REFERERhttp://test.chathispano.com/canal/sincensura/blog
HTTP_USER_AGENTMozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
HTTP_ACCEPT*/*
HTTP_HOSTtest.chathispano.com
DISABLE_ANTEVENIO0
DISABLE_MOBUSI0
DISABLE_MASSARIUS0
DISABLE_RELATEDCONTENT0
DISABLE_ADSENSE0
ADS_DEFER0
PHALCON_APPweb
CHATHISPANOENGINE_REVISIONdevelopment
CHATHISPANOENGINE_INSTANCEvirtualbox
SCRIPT_FILENAME/srv/ChatHispanoEngine/releases/94/public/index.php
PATH_TRANSLATED/srv/ChatHispanoEngine/current/public
PATH_INFO
REDIRECT_STATUS200
SERVER_NAMEtest.chathispano.com
SERVER_PORT80
SERVER_ADDR10.234.61.101
REMOTE_USER
REMOTE_PORT60850
REMOTE_ADDR10.234.61.52
SERVER_SOFTWAREnginx/1.22.1
GATEWAY_INTERFACECGI/1.1
REQUEST_SCHEMEhttp
SERVER_PROTOCOLHTTP/1.1
DOCUMENT_ROOT/srv/ChatHispanoEngine/releases/94/public
DOCUMENT_URI/index.php
REQUEST_URI/canal/sincensura/blog
SCRIPT_NAME/index.php
CONTENT_LENGTH
CONTENT_TYPE
REQUEST_METHODGET
QUERY_STRING_url=/canal/sincensura/blog
FCGI_ROLERESPONDER
PHP_SELF/index.php
REQUEST_TIME_FLOAT1715727686.4295
REQUEST_TIME1715727686
#Path
0/srv/ChatHispanoEngine/releases/94/public/index.php
1/srv/ChatHispanoEngine/releases/94/vendor/autoload.php
2/srv/ChatHispanoEngine/releases/94/vendor/composer/autoload_real.php
3/srv/ChatHispanoEngine/releases/94/vendor/composer/platform_check.php
4/srv/ChatHispanoEngine/releases/94/vendor/composer/ClassLoader.php
5/srv/ChatHispanoEngine/releases/94/vendor/composer/include_paths.php
6/srv/ChatHispanoEngine/releases/94/vendor/composer/autoload_static.php
7/srv/ChatHispanoEngine/releases/94/vendor/amphp/amp/lib/functions.php
8/srv/ChatHispanoEngine/releases/94/vendor/amphp/amp/lib/Internal/functions.php
9/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-mbstring/bootstrap.php
10/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-mbstring/bootstrap80.php
11/srv/ChatHispanoEngine/releases/94/vendor/react/promise/src/functions_include.php
12/srv/ChatHispanoEngine/releases/94/vendor/react/promise/src/functions.php
13/srv/ChatHispanoEngine/releases/94/vendor/amphp/byte-stream/lib/functions.php
14/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-ctype/bootstrap.php
15/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-ctype/bootstrap80.php
16/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-intl-grapheme/bootstrap.php
17/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-intl-normalizer/bootstrap.php
18/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php
19/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-php80/bootstrap.php
20/srv/ChatHispanoEngine/releases/94/vendor/symfony/string/Resources/functions.php
21/srv/ChatHispanoEngine/releases/94/vendor/amphp/process/lib/functions.php
22/srv/ChatHispanoEngine/releases/94/vendor/amphp/serialization/src/functions.php
23/srv/ChatHispanoEngine/releases/94/vendor/amphp/sync/src/functions.php
24/srv/ChatHispanoEngine/releases/94/vendor/amphp/sync/src/ConcurrentIterator/functions.php
25/srv/ChatHispanoEngine/releases/94/vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
26/srv/ChatHispanoEngine/releases/94/vendor/symfony/var-dumper/Resources/functions/dump.php
27/srv/ChatHispanoEngine/releases/94/vendor/daverandom/libdns/src/functions.php
28/srv/ChatHispanoEngine/releases/94/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php
29/srv/ChatHispanoEngine/releases/94/vendor/psy/psysh/src/functions.php
30/srv/ChatHispanoEngine/releases/94/vendor/symfony/polyfill-php81/bootstrap.php
31/srv/ChatHispanoEngine/releases/94/vendor/amphp/dns/lib/functions.php
32/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/functions.php
33/srv/ChatHispanoEngine/releases/94/vendor/swiftmailer/swiftmailer/lib/swift_required.php
34/srv/ChatHispanoEngine/releases/94/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php
35/srv/ChatHispanoEngine/releases/94/apps/Application.php
36/srv/ChatHispanoEngine/releases/94/config/config.php
37/srv/ChatHispanoEngine/releases/94/config/config_staging.php
38/srv/ChatHispanoEngine/releases/94/config/loader.php
39/srv/ChatHispanoEngine/releases/94/config/services.php
40/srv/ChatHispanoEngine/releases/94/config/managers.php
41/srv/ChatHispanoEngine/releases/94/config/routing.php
42/srv/ChatHispanoEngine/releases/94/apps/Web/config/routing.php
43/srv/ChatHispanoEngine/releases/94/apps/Web/Module.php
44/srv/ChatHispanoEngine/releases/94/apps/Web/config/config.php
45/srv/ChatHispanoEngine/releases/94/apps/Web/config/services.php
46/srv/ChatHispanoEngine/releases/94/apps/Web/config/managers.php
47/srv/ChatHispanoEngine/releases/94/apps/Web/Controllers/ChannelController.php
48/srv/ChatHispanoEngine/releases/94/apps/Web/Controllers/BaseController.php
49/srv/ChatHispanoEngine/releases/94/apps/Core/Controllers/BaseController.php
50/srv/ChatHispanoEngine/releases/94/vendor/iwalkalone/translator/src/iwalkalone/Translator.php
51/srv/ChatHispanoEngine/releases/94/apps/Web/Auth/Auth.php
52/srv/ChatHispanoEngine/releases/94/apps/Core/Managers/InspIRCd/GlineManager.php
53/srv/ChatHispanoEngine/releases/94/apps/Core/Models/InspIRCd/Gline.php
54/srv/ChatHispanoEngine/releases/94/apps/Core/Models/BaseCollection.php
55/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Client.php
56/srv/ChatHispanoEngine/releases/94/vendor/jean85/pretty-package-versions/src/PrettyVersions.php
57/srv/ChatHispanoEngine/releases/94/vendor/composer/InstalledVersions.php
58/srv/ChatHispanoEngine/releases/94/vendor/composer/installed.php
59/srv/ChatHispanoEngine/releases/94/vendor/jean85/pretty-package-versions/src/Version.php
60/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Database.php
61/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Collection.php
62/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Operation/FindOne.php
63/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Operation/Executable.php
64/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Operation/Explainable.php
65/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Operation/Find.php
66/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Model/BSONArray.php
67/srv/ChatHispanoEngine/releases/94/vendor/mongodb/mongodb/src/Model/BSONDocument.php
68/srv/ChatHispanoEngine/releases/94/apps/Core/Managers/InspIRCd/ChannelManager.php
69/srv/ChatHispanoEngine/releases/94/apps/Core/Models/Channel/Datasheet.php
Memory
Usage4194304