<commit_before><?php

namespace Minds\Core\Feeds\Elastic;

use Minds\Common\Repository\Response;
use Minds\Common\Urn;
use Minds\Core\Feeds\FeedSyncEntity;
use Minds\Core\Di\Di;
use Minds\Core\Events\Dispatcher;
use Minds\Core\Search;
use Minds\Entities\Entity;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Trending\Aggregates;
use Minds\Core\Security\ACL;
use Minds\Helpers\Flags;

class Manager
{
    /** @var Repository */
    protected $repository;

    /** @var Search\Search */
    private $search;

    /** @var Events\Dispatcher */
    private $eventsDispatcher;

    /** @var EntitiesBuilder */
    protected $entitiesBuilder;

    /** @var ACL */
    protected $acl;

    /** @var Entities */
    protected $entities;

    private $from;

    private $to;

    private $type = 'activity';

    private $subtype = '';

    public function __construct(
        $repository = null,
        $entitiesBuilder = null,
        $entities = null,
        $search = null,
        $eventsDispatcher = null,
        $acl = null
    ) {
        $this->repository = $repository ?: new Repository;
        $this->entitiesBuilder = $entitiesBuilder ?: new EntitiesBuilder;
        $this->entities = $entities ?: new Entities;
        $this->search = $search ?: Di::_()->get('Search\Search');
        $this->eventsDispatcher = $eventsDispatcher ?? Di::_()->get('EventsDispatcher');
        $this->acl = $acl ?? Di::_()->get('Security\ACL');

        $this->from = strtotime('-7 days') * 1000;
        $this->to = time() * 1000;
    }

    /**
     * @param string $type
     * @return Manager
     */
    public function setType($type)
    {
        $this->type = $type;
        return $this;
    }

    /**
     * @param string $subtype
     * @return Manager
     */
    public function setSubtype($subtype)
    {
        $this->subtype = $subtype;
        return $this;
    }

    /**
     * @param array $opts
     * @return Response
     * @throws \Exception
     */
    public function getList(array $opts = [])
    {
        // Mobile will temporarily disable new style reminds from being displayed (default true for mobile)
        $hide_reminds = filter_var($_GET['hide_reminds'] ?? isset($_SERVER['HTTP_APP_VERSION']), FILTER_VALIDATE_BOOLEAN);

        $opts = array_merge([
            'algorithm' => null,
            'cache_key' => null,
            'user_guid' => null,
            'container_guid' => null,
            'owner_guid' => null,
            'subscriptions' => null,
            'access_id' => null,
            'custom_type' => null,
            'offset' => 0,
            'limit' => 12,
            'type' => null,
            'sync' => false,
            'from_timestamp' => null,
            'reverse_sort' => null,
            'query' => null,
            'nsfw' => null,
            'single_owner_threshold' => 36,
            'filter_hashtags' => false,
            'pinned_guids' => null,
            'as_activities' => false,
            'exclude' => null,
            'pending' => false,
            'plus' => false,
            'hide_reminds' => $hide_reminds,
            'wire_support_tier_only' => false,
        ], $opts);

        if (isset($opts['query']) && $opts['query']) {
            $opts['query'] = str_replace('#', '', strtolower($opts['query']));
        }

        if (isset($opts['query']) && $opts['query'] && in_array($opts['type'], ['user', 'group'], true)) {
            $result = $this->search($opts);

            $response = new Response($result);
            return $response;
        }

        $feedSyncEntities = [];
        $scores = [];
        $owners = [];
        $i = 0;

        foreach ($this->repository->getList($opts) as $scoredGuid) {
            if (!$scoredGuid->getGuid()) {
                continue;
            }

            $ownerGuid = $scoredGuid->getOwnerGuid() ?: $scoredGuid->getGuid();

            if (
                $i < $opts['single_owner_threshold']
                && isset($owners[$ownerGuid])
                && !$opts['filter_hashtags']
                && !in_array($opts['type'], ['user', 'group'], true)
            ) {
                continue;
            }
            $owners[$ownerGuid] = true;

            ++$i; // Update here as we don't want to count skipped

            $entityType = $scoredGuid->getType() ?? 'entity';
            if (strpos($entityType, 'object-', 0) === 0) {
                $entityType = str_replace('object-', '', $entityType);
            }

            if ($opts['as_activities'] && !in_array($opts['type'], ['user', 'group'], true)) {
                $entityType = 'activity';
            }

            $urn = implode(':', [
                'urn',
                $entityType ?: 'entity',
                $scoredGuid->getGuid(),
            ]);

            $feedSyncEntities[] = (new FeedSyncEntity())
                ->setGuid((string) $scoredGuid->getGuid())
    
                 ->setOwnerGuid((string) $ownerGuid)
                ->setUrn(new Urn($urn))
                ->setTimestamp($scoredGuid->getTimestamp());

            $scores[(string) $scoredGuid->getGuid()] = $scoredGuid->getScore();
        }

        $entities = [];
        $next = '';

        if (count($feedSyncEntities) > 0) {
            $next = (string) (array_reduce($feedSyncEntities, function ($carry, FeedSyncEntity $feedSyncEntity) {
                return min($feedSyncEntity->getTimestamp() ?: INF, $carry);
            }, INF) - 1);

            $hydrateGuids = array_map(function (FeedSyncEntity $feedSyncEntity) {
                return $feedSyncEntity->getGuid();
            }, array_slice($feedSyncEntities, 0, 12)); // hydrate the first 12

            $hydratedEntities = $this->entitiesBuilder->get(['guids' => $hydrateGuids]);

            foreach ($hydratedEntities as $entity) {<commit_msg>(chore): force post on top of mobile newsfeeds to prompt upgrade<commit_after><?php

namespace Minds\Core\Feeds\Elastic;

use Composer\Semver\Comparator;
use Minds\Common\Repository\Response;
use Minds\Common\Urn;
use Minds\Core\Feeds\FeedSyncEntity;
use Minds\Core\Di\Di;
use Minds\Core\Events\Dispatcher;
use Minds\Core\Search;
use Minds\Entities\Entity;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Trending\Aggregates;
use Minds\Core\Security\ACL;
use Minds\Helpers\Flags;

class Manager
{
    /** @var Repository */
    protected $repository;

    /** @var Search\Search */
    private $search;

    /** @var Events\Dispatcher */
    private $eventsDispatcher;

    /** @var EntitiesBuilder */
    protected $entitiesBuilder;

    /** @var ACL */
    protected $acl;

    /** @var Entities */
    protected $entities;

    private $from;

    private $to;

    private $type = 'activity';

    private $subtype = '';

    public function __construct(
        $repository = null,
        $entitiesBuilder = null,
        $entities = null,
        $search = null,
        $eventsDispatcher = null,
        $acl = null
    ) {
        $this->repository = $repository ?: new Repository;
        $this->entitiesBuilder = $entitiesBuilder ?: new EntitiesBuilder;
        $this->entities = $entities ?: new Entities;
        $this->search = $search ?: Di::_()->get('Search\Search');
        $this->eventsDispatcher = $eventsDispatcher ?? Di::_()->get('EventsDispatcher');
        $this->acl = $acl ?? Di::_()->get('Security\ACL');

        $this->from = strtotime('-7 days') * 1000;
        $this->to = time() * 1000;
    }

    /**
     * @param string $type
     * @return Manager
     */
    public function setType($type)
    {
        $this->type = $type;
        return $this;
    }

    /**
     * @param string $subtype
     * @return Manager
     */
    public function setSubtype($subtype)
    {
        $this->subtype = $subtype;
        return $this;
    }

    /**
     * @param array $opts
     * @return Response
     * @throws \Exception
     */
    public function getList(array $opts = [])
    {
        // Mobile will temporarily disable new style reminds from being displayed (default true for mobile)
        $hide_reminds = filter_var($_GET['hide_reminds'] ?? isset($_SERVER['HTTP_APP_VERSION']), FILTER_VALIDATE_BOOLEAN);

        $opts = array_merge([
            'algorithm' => null,
            'cache_key' => null,
            'user_guid' => null,
            'container_guid' => null,
            'owner_guid' => null,
            'subscriptions' => null,
            'access_id' => null,
            'custom_type' => null,
            'offset' => 0,
            'limit' => 12,
            'type' => null,
            'sync' => false,
            'from_timestamp' => null,
            'reverse_sort' => null,
            'query' => null,
            'nsfw' => null,
            'single_owner_threshold' => 36,
            'filter_hashtags' => false,
            'pinned_guids' => null,
            'as_activities' => false,
            'exclude' => null,
            'pending' => false,
            'plus' => false,
            'hide_reminds' => $hide_reminds,
            'wire_support_tier_only' => false,
        ], $opts);

        if (isset($opts['query']) && $opts['query']) {
            $opts['query'] = str_replace('#', '', strtolower($opts['query']));
        }

        if (isset($opts['query']) && $opts['query'] && in_array($opts['type'], ['user', 'group'], true)) {
            $result = $this->search($opts);

            $response = new Response($result);
            return $response;
        }

        $feedSyncEntities = [];
        $scores = [];
        $owners = [];
        $i = 0;

        foreach ($this->repository->getList($opts) as $scoredGuid) {
            if (!$scoredGuid->getGuid()) {
                continue;
            }

            $ownerGuid = $scoredGuid->getOwnerGuid() ?: $scoredGuid->getGuid();

            if (
                $i < $opts['single_owner_threshold']
                && isset($owners[$ownerGuid])
                && !$opts['filter_hashtags']
                && !in_array($opts['type'], ['user', 'group'], true)
            ) {
                continue;
            }
            $owners[$ownerGuid] = true;

            ++$i; // Update here as we don't want to count skipped

            $entityType = $scoredGuid->getType() ?? 'entity';
            if (strpos($entityType, 'object-', 0) === 0) {
                $entityType = str_replace('object-', '', $entityType);
            }

            if ($opts['as_activities'] && !in_array($opts['type'], ['user', 'group'], true)) {
                $entityType = 'activity';
            }

            $urn = implode(':', [
                'urn',
                $entityType ?: 'entity',
                $scoredGuid->getGuid(),
            ]);

            $feedSyncEntities[] = (new FeedSyncEntity())
                ->setGuid((string) $scoredGuid->getGuid())
    
                 ->setOwnerGuid((string) $ownerGuid)
                ->setUrn(new Urn($urn))
                ->setTimestamp($scoredGuid->getTimestamp());

            $scores[(string) $scoredGuid->getGuid()] = $scoredGuid->getScore();
        }

        $entities = [];
        $next = '';

        /**
         * Awkward hack to pin mobile post
         */
        if (isset($_SERVER['HTTP_APP_VERSION']) && Comparator::lessThan($_SERVER['HTTP_APP_VERSION'], '4.17.0')) {
            $mobilePin = (new FeedSyncEntity())
                ->setGuid("1279518512628371457")
                ->setOwnerGuid("100000000000000519")
                ->setUrn(new Urn("urn:activity:1279518512628371457"))
                ->setTimestamp(1630436985);
            array_unshift($feedSyncEntities, $mobilePin);
        }

        if (count($feedSyncEntities) > 0) {
            $next = (string) (array_reduce($feedSyncEntities, function ($carry, FeedSyncEntity $feedSyncEntity) {
                return min($feedSyncEntity->getTimestamp() ?: INF, $carry);
            }, INF) - 1);

            $hydrateGuids = array_map(function (FeedSyncEntity $feedSyncEntity) {
                return $feedSyncEntity->getGuid();
            }, array_slice($feedSyncEntities, 0, 12)); // hydrate the first 12

            $hydratedEntities = $this->entitiesBuilder->get(['guids' => $hydrateGuids]);

            foreach ($hydratedEntities as $entity) {
{'fraction_non_alphanumeric': 0.15716386211403366, 'fraction_numerical': 0.009638947884332625, 'mean_word_length': 1.9451527543901852, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 127, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3196347', 'n_tokens_mistral': 3712, 'n_tokens_neox': 3539, 'n_words': 716}