Added polls and additional network functions

This commit is contained in:
aymm 2021-03-09 07:27:10 +00:00
parent d651feb33d
commit ce35f3e036
4 changed files with 364 additions and 2 deletions

View File

@ -3,6 +3,7 @@
namespace APnutI; namespace APnutI;
use APnutI\Entities\Post; use APnutI\Entities\Post;
use APnutI\Entities\Poll;
use APnutI\Entities\User; use APnutI\Entities\User;
use APnutI\Exceptions\PnutException; use APnutI\Exceptions\PnutException;
use APnutI\Exceptions\NotFoundException; use APnutI\Exceptions\NotFoundException;
@ -17,7 +18,7 @@ use Psr\Log\NullLogger;
class APnutI class APnutI
{ {
protected string $api_url = 'https://api.pnut.io/v1/'; protected string $api_url = 'https://api.pnut.io/v1';
protected string $auth_url = 'https://pnut.io/oauth/authenticate'; protected string $auth_url = 'https://pnut.io/oauth/authenticate';
protected string $client_secret; protected string $client_secret;
protected string $client_id; protected string $client_id;
@ -35,6 +36,7 @@ class APnutI
protected ?string $access_token; protected ?string $access_token;
protected LoggerInterface $logger; protected LoggerInterface $logger;
protected string $token_session_key; protected string $token_session_key;
protected string $token_redirect_after_auth;
protected ?string $server_token_file_path = null; protected ?string $server_token_file_path = null;
public ?Meta $meta = null; public ?Meta $meta = null;
@ -68,6 +70,8 @@ class APnutI
{ {
$this->logger = empty($log_path) ? new NullLogger() : new Logger($this->app_name); $this->logger = empty($log_path) ? new NullLogger() : new Logger($this->app_name);
$this->token_session_key = $this->app_name.'access_token'; $this->token_session_key = $this->app_name.'access_token';
$this->token_redirect_after_auth = $this->app_name
.'redirect_after_auth';
$handler = new RotatingFileHandler($log_path, 5, Logger::DEBUG, true); $handler = new RotatingFileHandler($log_path, 5, Logger::DEBUG, true);
$this->logger->pushHandler($handler); $this->logger->pushHandler($handler);
$this->server_token = null; $this->server_token = null;
@ -297,7 +301,183 @@ class APnutI
"Checking auth status for app: {$this->app_name}: {$log_str}" "Checking auth status for app: {$this->app_name}: {$log_str}"
); );
$this->logger->info('Referrer: '.($_SERVER['HTTP_REFERER'] ?? 'Unknown')); $this->logger->info('Referrer: '.($_SERVER['HTTP_REFERER'] ?? 'Unknown'));
$_SESSION['redirect_after_auth'] = $_SERVER['HTTP_REFERER']; $_SESSION[$this->token_redirect_after_auth] = $_SERVER['HTTP_REFERER'];
return $is_authenticated; return $is_authenticated;
} }
public function authenticate(string $auth_code): bool
{
$this->logger->debug("Authenticating: {$auth_code}");
$parameters = [
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'code' => $auth_code,
'redirect_uri' => $this->redirect_uri,
'grant_type'=> 'authorization_code'
];
$resp = $this->post(
'/oauth/access_token',
$parameters,
'application/x-www-form-urlencoded'
);
if ($resp === null || !isset($resp['access_token'])) {
$this->logger->error("No access token ".json_encode($resp));
return false;
} else {
$this->logger->debug('Received access token ' . $resp['access_token']);
$_SESSION[$this->token_session_key] = $resp['access_token'];
$this->logger->debug('Saved access token');
$this->access_token = $resp['access_token'];
return true;
}
}
public function logout()
{
unset($_SESSION[$this->token_session_key]);
$this->access_token = null;
}
// TODO
public function getPostsForUser(User $user, $count = null)
{
}
// TODO
public function getPostsForUsername(string $username, int $count = 0)
{
/*
if(!$this->isAuthenticated()) {
throw new NotAuthorizedException("Cannot retrieve posts, ");
}
*/
if (mb_substr($username, 0, 1) !== '@') {
$username = '@'.$username;
}
$params = [];
if ($count > 0) {
$params['count'] = $count;
}
$posts = $this->get('/users/' . $username . '/posts', $params);
$p = [];
foreach ($posts as $post) {
$p[] = new Post($post);
}
var_dump($p);
}
public function searchPosts(
array $args,
bool $order_by_id = true,
int $count = 0
): array {
if ($order_by_id) {
$args['order'] = 'id';
}
if ($count > 0) {
$args['count'] = $count;
}
$post_obj = [];
/*
* Stop fetching if:
* - count($posts) >= $count and $count != 0
* - OR: meta['more'] is false
*/
do {
$posts = $this->get('/posts/search', $args);
if ($this->meta->more) {
$args['before_id'] = $this->meta->min_id;
}
foreach ($posts as $post) {
$post_obj[] = new Post($post);
}
} while ($this->meta != null
&& $this->meta->more
&& (count($post_obj) < $count || $count !== 0));
return $post_obj;
}
// TODO Maybe support additional polls?
public function getPollsFromUser(int $user_id, array $params = []): array
{
$parameters = [
'raw_types' => 'io.pnut.core.poll-notice',
'creator_id' => $user_id,
'include_deleted' => false,
'include_client' => false,
'include_counts' => false,
'include_html' => false,
'include_mention_posts' => false,
'include_copy_mentions' => false,
'include_post_raw' => true
];
foreach ($params as $param => $value) {
$parameters[$param] = $value;
}
$response = $this->get('posts/search', $parameters);
if (count($response) === 0) {
return [];
}
$polls = [];
foreach ($response as $post) {
if (!empty($post['raw'])) {
foreach ($post['raw'] as $raw) {
if (Poll::$notice_type === $raw['type']) {
$polls[] = $this->getPoll($raw['value']['poll_id']);
}
}
}
}
return $polls;
}
public function getPoll(int $poll_id): Poll
{
return new Poll($this->get('/polls/' . $poll_id));
}
public function getAuthorizedUser(): User
{
return $this->getUser('/me');
}
public function getUser(int $user_id, array $args = [])
{
return new User($this->get('/users/'.$user_id, $args));
}
public function getPost(int $post_id, array $args = [])
{
if (!empty($this->access_token)) {
#$this->logger->info("AT:".$this->access_token);
} else {
$this->logger->info("No AT");
}
// Remove in p roduction again
try {
$p = new Post($this->get('/posts/'.$post_id, $args));
$this->logger->debug(json_encode($p));
return $p;
} catch (NotAuthorizedException $nae) {
$this->logger->warning(
'NotAuthorizedException when getting post, trying without access token'
);
//try again not authorized
$r = $this->make_request(
'/get',
'/posts/' . $post_id,
$args,
'application/json',
true
);
return new Post($r);
}
}
public function getAvatar(int $user_id, array $args = []): string
{
return $this->get('/users/'.$user_id.'/avatar', $args);
}
} }

110
src/Entities/Poll.php Normal file
View File

@ -0,0 +1,110 @@
<?php
namespace APnutI\Entities;
use APnutI\Entities\PollOption;
use APnutI\Entities\User;
use APnutI\Entities\Source;
use APnutI\Exceptions\NotSupportedPollException;
class Poll
{
public \DateTime $created_at;
public \DateTime $closed_at;
public int $id = 0;
public bool $is_anonymous = false;
public bool $is_public = false;
public array $options = [];
public ?string $token = null;
public string $prompt = "";
public ?User $user = null;
public ?Source $source = null;
public string $type;
public static string $notice_type = 'io.pnut.core.poll-notice';
protected static array $poll_types = [
'general.poll',
'net.unsweets.beta',
'io.pnut.core.poll'
];
public function __construct(array $data)
{
$this->options = [];
$this->type = $data['type'];
if ($data['type'] === Poll::$notice_type) {
$val = $data['value'];
$this->closed_at = new \DateTime($val['closed_at']);
foreach ($val['options'] as $option) {
$this->options[] = new PollOption($option);
}
$this->id = (int)$val['poll_id'];
$this->token = $val['poll_token'];
$this->prompt = $val['prompt'];
} elseif (in_array($data['type'], Poll::$poll_types)) {
$this->created_at = new \DateTime($data['created_at']);
$this->closed_at = new \DateTime($data['closed_at']);
$this->id = (int)$data['id'];
$this->is_anonymous = (bool)$data['is_anonymous'];
$this->is_public = (bool)$data['is_public'];
foreach ($data['options'] as $option) {
$this->options[] = new PollOption($option);
}
if (!empty($data['poll_token'])) {
$this->token = $data['poll_token'];
}
$this->prompt = $data['prompt'];
if (!empty($data['user'])) {
$this->user = new User($data['user']);
}
if (!empty($data['source'])) {
$this->source = new Source($data['source']);
}
} else {
throw new NotSupportedPollException($data['type']);
}
}
/**
* Returns the most voted option. If multiple options have the same amount
* of voted, return all of them. Always returns an array!
*/
public function getMostVotedOption(): array
{
if (count($this->options) === 0) {
return [];
}
$optns = [];
//$most_voted_option = $this->options[0];
$most_voted_option = null;
foreach ($this->options as $option) {
if ($option->greaterThan($most_voted_option)) {
$optns = [];
$most_voted_option = $option;
$optns[] = $option;
} elseif ($option->greaterThanOrSame($most_voted_option)) {
$optns[] = $option;
}
}
return $optns;
}
public static function isValidPoll(string $type): bool
{
return $type === Poll::$notice_type || in_array($type, Poll::$poll_types);
}
public function __toString(): string
{
if (!empty($this->user)) {
$str = $this->user->username;
#$str = 'Unknown user';
} else {
$str = 'Unknown user';
}
return $str
. " asked: '"
. $this->prompt
. "', closed at "
. $this->closed_at->format('Y-m-d H:i:s T');
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace APnutI\Entities;
class PollOption
{
public string $text;
public int $position;
public bool $is_your_response = false;
public int $respondents = 0;
public array $respondent_ids = [];
public function __construct($data)
{
$this->text = $data['text'];
$this->position = (int)$data['position'];
if (!empty($data['is_your_response'])) {
$this->is_your_response = (bool)$data['is_your_response'];
}
if (!empty($data['respondents'])) {
$this->respondents = (int)$data['respondents'];
}
if (!empty($data['respondent_ids'])) {
$this->respondent_ids = $data['respondent_ids'];
}
}
public function greaterThan(?PollOption $option): bool
{
return empty($option)
|| ($this->text != $option->text
&& $this->respondents > $option->respondents);
}
public function greaterThanOrSame(?PollOption $option): bool
{
return empty($option)
|| ($this->text != $option->text
&& $this->respondents >= $option->respondents);
}
public function smallerThan(?PollOption $option): bool
{
return empty($option)
|| ($this->text != $option->text
&& $this->respondents < $option->respondents);
}
public function smallerThanOrSame(?PollOption $option): bool
{
return empty($option)
|| ($this->text != $option->text
&& $this->respondents <= $option->respondents);
}
public function equals(?PollOption $option): bool
{
return $this->text == $option->text
&& $this->respondents == $option->respondents;
}
public function __toString(): string
{
return $this->text . ' with ' . $this->respondents .' respondents';
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace APnutI\Exceptions;
class NotSupportedPollException extends Exception
{
}