Loading Controllers/Cli/Moderation.php +7 −2 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter $reportUrn = $this->getOpt('report'); $juryType = $this->getOpt('jury-type') ?? null; $respond = $this->getOpt('respond') ?? null; $activeThreshold = $this->getOpt('active-threshold') ?? 5 * 60; if (!$userId || !$reportUrn) { $this->out([ Loading Loading @@ -79,9 +80,13 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter $appeal = new Core\Reports\Appeals\Appeal(); $appeal->setReport($report); $summonsManager->summon($appeal, [ $user->guid ]); $missing = $summonsManager->summon($appeal, [ 'include_only' => [ (string) $user->guid ], 'active_threshold' => (int) $activeThreshold, ]); $this->out("Summoned {$user->guid} to {$reportUrn}"); $this->out("${missing} juror(s) missing."); } else { $summons = new Summons(); $summons Loading Loading @@ -128,7 +133,7 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter ->setReport($report) ->setOwnerGuid($report->getEntityOwnerGuid()); $cohort = $summonsManager->summon($appeal, null); $cohort = $summonsManager->summon($appeal); var_dump($cohort); } Loading Core/Queue/Runners/ReportsAppealSummon.php +3 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,9 @@ class ReportsAppealSummon implements QueueRunner /** @var Manager $manager */ $manager = Di::_()->get('Moderation\Summons\Manager'); $missing = $manager->summon($appeal, $cohort); $missing = $manager->summon($appeal, [ 'include_only' => $cohort ?: null, ]); if ($missing > 0) { echo "Missing {$missing} juror(s). Deferring..." . PHP_EOL; Loading Core/Reports/Summons/Cohort.php +9 −8 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ class Cohort $this->repository = $repository ?: new Repository(); $this->pool = $pool ?: new Pool(); $this->poolSize = $poolSize ?: 400; $this->maxPages = $maxPages ?: 1; // NOTE: Normally capped to 20. $this->maxPages = $maxPages ?: 2; // NOTE: Normally capped to 20. } /** Loading @@ -54,6 +54,8 @@ class Cohort 'size' => 0, 'for' => null, 'except' => [], 'except_hashes' => [], 'include_only' => null, 'active_threshold' => null, ], $opts); Loading @@ -62,9 +64,9 @@ class Cohort $page = 0; while (true) { if ($page > $this->maxPages) { if ($page >= $this->maxPages) { // Max = PoolSize * MaxPages error_log('Cannot gather a cohort'); error_log("Warning: Cannot gather a full cohort on {$this->maxPages} partitions"); break; } Loading @@ -73,16 +75,15 @@ class Cohort 'platform' => 'browser', 'for' => $opts['for'], 'except' => $opts['except'], 'except_hashes' => $opts['except_hashes'], 'include_only' => $opts['include_only'], 'validated' => true, 'page' => $page, 'size' => $this->poolSize, 'page' => $page, 'max_pages' => $this->maxPages, ]); $j = 0; foreach ($pool as $userGuid) { $j++; // TODO: Check subs $cohort[] = $userGuid; Loading @@ -91,7 +92,7 @@ class Cohort } } if ($j === 0 || count($cohort) >= $opts['size']) { if (count($cohort) >= $opts['size']) { break; } Loading Core/Reports/Summons/Manager.php +62 −37 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ use Minds\Core\Queue\Runners\ReportsAppealSummon; use Minds\Core\Reports\Appeals\Appeal; use Minds\Core\Reports\Summons\Delegates; use Minds\Core\Reports\Manager as ReportsManager; use Minds\Core\Reports\UserReports\UserReport; use Minds\Helpers\Text; class Manager { Loading @@ -36,6 +38,7 @@ class Manager * Manager constructor. * @param Cohort $cohort * @param Repository $repository * @param ReportsManager $reportsManager * @param QueueClient $queueClient * @param Delegates\SocketDelegate $socketDelegate * @throws Exception Loading @@ -50,37 +53,44 @@ class Manager { $this->cohort = $cohort ?: new Cohort(); $this->repository = $repository ?: new Repository(); $this->reportsManager = $reportsManager ?: new ReportsManager; $this->reportsManager = $reportsManager ?: new ReportsManager(); $this->queueClient = $queueClient ?: Client::build(); $this->socketDelegate = $socketDelegate ?: new Delegates\SocketDelegate(); } /** * @param Appeal $appeal * @param array $cohort * @param array $opts * @return int * @throws Exception */ public function summon(Appeal $appeal, $cohort = null) public function summon(Appeal $appeal, array $opts = []) { $opts = array_merge([ 'include_only' => null, 'active_threshold' => 5 * 60, 'jury_size' => 12, 'awaiting_ttl' => 120, ], $opts); // Get a fresh report to collect completed jurors $report = $report = $this->reportsManager->getReport($appeal->getReport()->getUrn()); $reportUrn = $report->getUrn(); $juryType = 'appeal_jury'; $missing = 0; $completedJurorGuids = array_map(function($decision) { return $decision->getJurorGuid(); }, array_merge($report->getAppealJuryDecisions() ?: [], $report->getInitialJuryDecisions() ?: [])); // Get all summonses for this case if (!$cohort) { $summonses = iterator_to_array($this->repository->getList([ 'report_urn' => $reportUrn, 'jury_type' => $juryType, ])); $completedJurorGuids = array_map(function($decision) { return $decision->getJurorGuid(); }, array_merge($report->getAppealJuryDecisions(), $report->getInitialJuryDecisions())); // Remove the summons of jurors who have already voted // Remove the summonses of jurors who have already voted $summonses = array_filter($summonses, function (Summons $summons) use ($completedJurorGuids) { return !in_array($summons->getJurorGuid(), $completedJurorGuids); Loading @@ -88,11 +98,9 @@ class Manager // Check how many are missing $notDeclined = array_filter($summonses, function (Summons $summons) { $missing = $opts['jury_size'] - count(array_filter($summonses, function (Summons $summons) { return $summons->isAccepted() || $summons->isAwaiting(); }); $missing = 12 - count($notDeclined); })); // If we have a full jury, don't summon Loading @@ -100,19 +108,34 @@ class Manager return 0; } // Reduce jury to juror guids and try to pick up to missing size // Create an array of channel GUIDs that are involved in this case $pendingJurorGuids = array_map(function (Summons $summons) { $alreadyInvolvedGuids = array_map(function (Summons $summons) { return (string) $summons->getJurorGuid(); }, $summonses); $alreadyInvolvedGuids = array_merge($alreadyInvolvedGuids, array_map(function (UserReport $userReport) { return $userReport->getReporterGuid(); }, $report->getReports())); $alreadyInvolvedGuids = array_values(array_unique(Text::buildArray($alreadyInvolvedGuids))); // Create an array of channel phone hashes that are involved in this case $alreadyInvolvedPhoneHashes = $report->getUserHashes() ?: []; // Pick up to missing size $cohort = $this->cohort->pick([ 'size' => $missing, 'for' => $appeal->getOwnerGuid(), 'except' => $pendingJurorGuids, 'active_threshold' => 5 * 60, 'except' => $alreadyInvolvedGuids, 'except_hashes' => $alreadyInvolvedPhoneHashes, 'include_only' => $opts['include_only'], 'active_threshold' => $opts['active_threshold'], ]); } // Build Summonses foreach ($cohort as $juror) { $summons = new Summons(); Loading @@ -120,13 +143,15 @@ class Manager ->setReportUrn($reportUrn) ->setJuryType($juryType) ->setJurorGuid($juror) ->setTtl(120) ->setTtl($opts['awaiting_ttl']) ->setStatus('awaiting'); $this->repository->add($summons); $this->socketDelegate->onSummon($summons); } // return $missing; } Loading Core/Reports/Summons/Pool.php +28 −1 Original line number Diff line number Diff line Loading @@ -43,9 +43,12 @@ class Pool public function getList(array $opts = []) { $opts = array_merge([ 'for' => null, 'active_threshold' => 0, 'platform' => null, 'for' => null, 'except' => null, 'except_hashes' => null, 'include_only' => null, 'validated' => false, 'size' => 10, 'page' => 0, Loading Loading @@ -123,7 +126,19 @@ class Pool ]; } if ($opts['include_only']) { $body['query']['bool']['must'][] = [ 'terms' => [ 'user_guid' => $opts['include_only'], ], ]; } if ($opts['except']) { if (!isset($body['query']['bool']['must_not'])) { $body['query']['bool']['must_not'] = []; } $body['query']['bool']['must_not'][] = [ 'terms' => [ 'user_guid' => $opts['except'], Loading @@ -131,6 +146,18 @@ class Pool ]; } if ($opts['except_hashes']) { if (!isset($body['query']['bool']['must_not'])) { $body['query']['bool']['must_not'] = []; } $body['query']['bool']['must_not'][] = [ 'terms' => [ 'user_phone_number_hash' => $opts['except_hashes'], ], ]; } if ($opts['validated']) { $body['query']['bool']['must'][] = [ 'exists' => [ Loading Loading
Controllers/Cli/Moderation.php +7 −2 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter $reportUrn = $this->getOpt('report'); $juryType = $this->getOpt('jury-type') ?? null; $respond = $this->getOpt('respond') ?? null; $activeThreshold = $this->getOpt('active-threshold') ?? 5 * 60; if (!$userId || !$reportUrn) { $this->out([ Loading Loading @@ -79,9 +80,13 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter $appeal = new Core\Reports\Appeals\Appeal(); $appeal->setReport($report); $summonsManager->summon($appeal, [ $user->guid ]); $missing = $summonsManager->summon($appeal, [ 'include_only' => [ (string) $user->guid ], 'active_threshold' => (int) $activeThreshold, ]); $this->out("Summoned {$user->guid} to {$reportUrn}"); $this->out("${missing} juror(s) missing."); } else { $summons = new Summons(); $summons Loading Loading @@ -128,7 +133,7 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter ->setReport($report) ->setOwnerGuid($report->getEntityOwnerGuid()); $cohort = $summonsManager->summon($appeal, null); $cohort = $summonsManager->summon($appeal); var_dump($cohort); } Loading
Core/Queue/Runners/ReportsAppealSummon.php +3 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,9 @@ class ReportsAppealSummon implements QueueRunner /** @var Manager $manager */ $manager = Di::_()->get('Moderation\Summons\Manager'); $missing = $manager->summon($appeal, $cohort); $missing = $manager->summon($appeal, [ 'include_only' => $cohort ?: null, ]); if ($missing > 0) { echo "Missing {$missing} juror(s). Deferring..." . PHP_EOL; Loading
Core/Reports/Summons/Cohort.php +9 −8 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ class Cohort $this->repository = $repository ?: new Repository(); $this->pool = $pool ?: new Pool(); $this->poolSize = $poolSize ?: 400; $this->maxPages = $maxPages ?: 1; // NOTE: Normally capped to 20. $this->maxPages = $maxPages ?: 2; // NOTE: Normally capped to 20. } /** Loading @@ -54,6 +54,8 @@ class Cohort 'size' => 0, 'for' => null, 'except' => [], 'except_hashes' => [], 'include_only' => null, 'active_threshold' => null, ], $opts); Loading @@ -62,9 +64,9 @@ class Cohort $page = 0; while (true) { if ($page > $this->maxPages) { if ($page >= $this->maxPages) { // Max = PoolSize * MaxPages error_log('Cannot gather a cohort'); error_log("Warning: Cannot gather a full cohort on {$this->maxPages} partitions"); break; } Loading @@ -73,16 +75,15 @@ class Cohort 'platform' => 'browser', 'for' => $opts['for'], 'except' => $opts['except'], 'except_hashes' => $opts['except_hashes'], 'include_only' => $opts['include_only'], 'validated' => true, 'page' => $page, 'size' => $this->poolSize, 'page' => $page, 'max_pages' => $this->maxPages, ]); $j = 0; foreach ($pool as $userGuid) { $j++; // TODO: Check subs $cohort[] = $userGuid; Loading @@ -91,7 +92,7 @@ class Cohort } } if ($j === 0 || count($cohort) >= $opts['size']) { if (count($cohort) >= $opts['size']) { break; } Loading
Core/Reports/Summons/Manager.php +62 −37 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ use Minds\Core\Queue\Runners\ReportsAppealSummon; use Minds\Core\Reports\Appeals\Appeal; use Minds\Core\Reports\Summons\Delegates; use Minds\Core\Reports\Manager as ReportsManager; use Minds\Core\Reports\UserReports\UserReport; use Minds\Helpers\Text; class Manager { Loading @@ -36,6 +38,7 @@ class Manager * Manager constructor. * @param Cohort $cohort * @param Repository $repository * @param ReportsManager $reportsManager * @param QueueClient $queueClient * @param Delegates\SocketDelegate $socketDelegate * @throws Exception Loading @@ -50,37 +53,44 @@ class Manager { $this->cohort = $cohort ?: new Cohort(); $this->repository = $repository ?: new Repository(); $this->reportsManager = $reportsManager ?: new ReportsManager; $this->reportsManager = $reportsManager ?: new ReportsManager(); $this->queueClient = $queueClient ?: Client::build(); $this->socketDelegate = $socketDelegate ?: new Delegates\SocketDelegate(); } /** * @param Appeal $appeal * @param array $cohort * @param array $opts * @return int * @throws Exception */ public function summon(Appeal $appeal, $cohort = null) public function summon(Appeal $appeal, array $opts = []) { $opts = array_merge([ 'include_only' => null, 'active_threshold' => 5 * 60, 'jury_size' => 12, 'awaiting_ttl' => 120, ], $opts); // Get a fresh report to collect completed jurors $report = $report = $this->reportsManager->getReport($appeal->getReport()->getUrn()); $reportUrn = $report->getUrn(); $juryType = 'appeal_jury'; $missing = 0; $completedJurorGuids = array_map(function($decision) { return $decision->getJurorGuid(); }, array_merge($report->getAppealJuryDecisions() ?: [], $report->getInitialJuryDecisions() ?: [])); // Get all summonses for this case if (!$cohort) { $summonses = iterator_to_array($this->repository->getList([ 'report_urn' => $reportUrn, 'jury_type' => $juryType, ])); $completedJurorGuids = array_map(function($decision) { return $decision->getJurorGuid(); }, array_merge($report->getAppealJuryDecisions(), $report->getInitialJuryDecisions())); // Remove the summons of jurors who have already voted // Remove the summonses of jurors who have already voted $summonses = array_filter($summonses, function (Summons $summons) use ($completedJurorGuids) { return !in_array($summons->getJurorGuid(), $completedJurorGuids); Loading @@ -88,11 +98,9 @@ class Manager // Check how many are missing $notDeclined = array_filter($summonses, function (Summons $summons) { $missing = $opts['jury_size'] - count(array_filter($summonses, function (Summons $summons) { return $summons->isAccepted() || $summons->isAwaiting(); }); $missing = 12 - count($notDeclined); })); // If we have a full jury, don't summon Loading @@ -100,19 +108,34 @@ class Manager return 0; } // Reduce jury to juror guids and try to pick up to missing size // Create an array of channel GUIDs that are involved in this case $pendingJurorGuids = array_map(function (Summons $summons) { $alreadyInvolvedGuids = array_map(function (Summons $summons) { return (string) $summons->getJurorGuid(); }, $summonses); $alreadyInvolvedGuids = array_merge($alreadyInvolvedGuids, array_map(function (UserReport $userReport) { return $userReport->getReporterGuid(); }, $report->getReports())); $alreadyInvolvedGuids = array_values(array_unique(Text::buildArray($alreadyInvolvedGuids))); // Create an array of channel phone hashes that are involved in this case $alreadyInvolvedPhoneHashes = $report->getUserHashes() ?: []; // Pick up to missing size $cohort = $this->cohort->pick([ 'size' => $missing, 'for' => $appeal->getOwnerGuid(), 'except' => $pendingJurorGuids, 'active_threshold' => 5 * 60, 'except' => $alreadyInvolvedGuids, 'except_hashes' => $alreadyInvolvedPhoneHashes, 'include_only' => $opts['include_only'], 'active_threshold' => $opts['active_threshold'], ]); } // Build Summonses foreach ($cohort as $juror) { $summons = new Summons(); Loading @@ -120,13 +143,15 @@ class Manager ->setReportUrn($reportUrn) ->setJuryType($juryType) ->setJurorGuid($juror) ->setTtl(120) ->setTtl($opts['awaiting_ttl']) ->setStatus('awaiting'); $this->repository->add($summons); $this->socketDelegate->onSummon($summons); } // return $missing; } Loading
Core/Reports/Summons/Pool.php +28 −1 Original line number Diff line number Diff line Loading @@ -43,9 +43,12 @@ class Pool public function getList(array $opts = []) { $opts = array_merge([ 'for' => null, 'active_threshold' => 0, 'platform' => null, 'for' => null, 'except' => null, 'except_hashes' => null, 'include_only' => null, 'validated' => false, 'size' => 10, 'page' => 0, Loading Loading @@ -123,7 +126,19 @@ class Pool ]; } if ($opts['include_only']) { $body['query']['bool']['must'][] = [ 'terms' => [ 'user_guid' => $opts['include_only'], ], ]; } if ($opts['except']) { if (!isset($body['query']['bool']['must_not'])) { $body['query']['bool']['must_not'] = []; } $body['query']['bool']['must_not'][] = [ 'terms' => [ 'user_guid' => $opts['except'], Loading @@ -131,6 +146,18 @@ class Pool ]; } if ($opts['except_hashes']) { if (!isset($body['query']['bool']['must_not'])) { $body['query']['bool']['must_not'] = []; } $body['query']['bool']['must_not'][] = [ 'terms' => [ 'user_phone_number_hash' => $opts['except_hashes'], ], ]; } if ($opts['validated']) { $body['query']['bool']['must'][] = [ 'exists' => [ Loading