initial commit
This commit is contained in:
commit
e57af3ad36
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
history.json
|
735
Check_PCA.php
Normal file
735
Check_PCA.php
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PCA;
|
||||||
|
|
||||||
|
function write_pca_log($string, $level = 'INFO')
|
||||||
|
{
|
||||||
|
echo '[' . $level . '] ' . date('H:i:s') . ' ' . $string . PHP_EOL . '<br />';
|
||||||
|
file_put_contents(
|
||||||
|
get_log_file(),
|
||||||
|
'[' . $level . '] ' . date('H:i:s') . ' ' . $string . PHP_EOL,
|
||||||
|
FILE_APPEND | LOCK_EX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_log_file()
|
||||||
|
{
|
||||||
|
return realpath(__DIR__ . '/../pca_logs') . '/pca.log';
|
||||||
|
//return realpath(__DIR__ . '/../pca_logs') . '/' . date('Y-m-d') . '.log';
|
||||||
|
}
|
||||||
|
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
public $user_name;
|
||||||
|
public $name;
|
||||||
|
public $number_posts;
|
||||||
|
public $following;
|
||||||
|
public $follows_bot;
|
||||||
|
public $last_club_notification = '';
|
||||||
|
|
||||||
|
private static function clean_api_response($api_reponse)
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
if (array_key_exists('data', $api_reponse)) {
|
||||||
|
$data = $api_reponse['data'];
|
||||||
|
} else {
|
||||||
|
$data = $api_reponse;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($api_reponse)
|
||||||
|
{
|
||||||
|
$data = self::clean_api_response($api_reponse);
|
||||||
|
$this->user_name = '@' . $data['username'];
|
||||||
|
$this->number_posts = preg_replace('/\s+/', '', $data['counts']['posts']);
|
||||||
|
$this->follows_bot = $data['follows_you'];
|
||||||
|
$this->name = array_key_exists('name', $data) ? $data['name'] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_users_from_api_reponse($api_reponse)
|
||||||
|
{
|
||||||
|
$data = self::clean_api_response($api_reponse);
|
||||||
|
$users = [];
|
||||||
|
// Single user
|
||||||
|
if (count(array_filter(array_keys($data), 'is_string')) > 0) {
|
||||||
|
$users[] = new User($data);
|
||||||
|
} else {
|
||||||
|
// Multiple users
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$users[] = new User($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_highest_pca($clubs)
|
||||||
|
{
|
||||||
|
$key = $this->get_next_pca_key($clubs) - 1;
|
||||||
|
if ($key == -1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return $clubs[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_next_pca($clubs)
|
||||||
|
{
|
||||||
|
return $clubs[$this->get_next_pca_key($clubs)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_next_pca_key($clubs)
|
||||||
|
{
|
||||||
|
if (preg_replace('/\s+/', '', $clubs[0]['post_count']) > $this->number_posts
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
foreach ($clubs as $key => $club) {
|
||||||
|
if (preg_replace('/\s+/', '', $club['post_count']) > $this->number_posts
|
||||||
|
) {
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class API
|
||||||
|
{
|
||||||
|
public $access_token = '';
|
||||||
|
private static $api_endpoint = 'https://api.pnut.io/v1';
|
||||||
|
public $max_posttext_length = 256;
|
||||||
|
private static $settings_file_location = __DIR__
|
||||||
|
. '/..'
|
||||||
|
. '/pca_settings/user_settings.json';
|
||||||
|
private static $settings = [];
|
||||||
|
private static $default_notification_text = 'Congratulations {user.username}, you are now a member of #{pca.name} {pca.emoji} ({pca.postcount} posts)! Next: {nextpca.emoji} at {nextpca.postcount} posts';
|
||||||
|
private static $notification_tokens = [
|
||||||
|
'{user.username}',
|
||||||
|
'{user.name}',
|
||||||
|
'{pca.name}',
|
||||||
|
'{pca.emoji}',
|
||||||
|
'{pca.postcount}',
|
||||||
|
'{nextpca.name}',
|
||||||
|
'{nextpca.emoji}',
|
||||||
|
'{nextpca.postcount}',
|
||||||
|
'{posts_to_pca}',
|
||||||
|
];
|
||||||
|
public $me;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
write_pca_log('');
|
||||||
|
write_pca_log('=================================');
|
||||||
|
write_pca_log('');
|
||||||
|
$this->max_posttext_length = $this->get_data(
|
||||||
|
'https://api.pnut.io/v1/sys/config'
|
||||||
|
)['data']['post']['max_length'];
|
||||||
|
$this->me = User::get_users_from_api_reponse(
|
||||||
|
$this->get_data('https://api.pnut.io/v1/users/me')
|
||||||
|
)[0];
|
||||||
|
write_pca_log('Hi, my name is ' . $this->me->user_name, 'DEBUG');
|
||||||
|
$sapi_type = php_sapi_name();
|
||||||
|
$mode = !empty($_SERVER['DOCUMENT_ROOT']) &&
|
||||||
|
(substr($sapi_type, 0, 3) == 'cli' || empty($_SERVER['REMOTE_ADDR']))
|
||||||
|
? 'Debug'
|
||||||
|
: 'Production';
|
||||||
|
write_pca_log('I am currently in ' . $mode . ' mode');
|
||||||
|
if (!file_exists(self::$settings_file_location)) {
|
||||||
|
file_put_contents(self::$settings_file_location, json_encode([]));
|
||||||
|
write_pca_log(
|
||||||
|
'No user settings file found. Creating empty one at ' .
|
||||||
|
self::$settings_file_location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self::$settings = json_decode(
|
||||||
|
file_get_contents(self::$settings_file_location),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
write_pca_log('User settings file loaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_data(
|
||||||
|
$endpoint,
|
||||||
|
$parameters = [],
|
||||||
|
$method = 'GET',
|
||||||
|
$contenttype = 'application/x-www-form-urlencoded',
|
||||||
|
$force = false
|
||||||
|
) {
|
||||||
|
write_pca_log('Making request to pnut API at ' . $endpoint);
|
||||||
|
$postdata = $this->build_query_string($parameters);
|
||||||
|
if ($contenttype == 'application/json') {
|
||||||
|
$postdata = json_encode($parameters);
|
||||||
|
}
|
||||||
|
$context_options = [
|
||||||
|
'http' => [
|
||||||
|
'method' => $method,
|
||||||
|
'header' =>
|
||||||
|
'Content-type: ' .
|
||||||
|
$contenttype .
|
||||||
|
"\r\n" .
|
||||||
|
'Authorization: Bearer ' .
|
||||||
|
$this->access_token,
|
||||||
|
'content' => $postdata,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$sapi_type = php_sapi_name();
|
||||||
|
if (!$force
|
||||||
|
&& !empty($_SERVER['DOCUMENT_ROOT'])
|
||||||
|
&& (substr($sapi_type, 0, 3) == 'cli' || empty($_SERVER['REMOTE_ADDR']))
|
||||||
|
&& $method == 'POST'
|
||||||
|
) {
|
||||||
|
write_pca_log(
|
||||||
|
'Running from shell instead of server. Debug mode assumed. Not submitting POST requests to server!',
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
write_pca_log(
|
||||||
|
'Would have posted if run on a server: ' . $postdata,
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$context = stream_context_create($context_options);
|
||||||
|
$response = @file_get_contents($endpoint, false, $context);
|
||||||
|
if ($response === false) {
|
||||||
|
write_pca_log('Received no or invalid response from server', 'ERROR');
|
||||||
|
return [
|
||||||
|
'meta' => ['code' => '401', 'error_message' => 'Invalid request'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$resp_dict = json_decode($response, true);
|
||||||
|
write_pca_log(
|
||||||
|
'Got server response. Meta: ' . json_encode($resp_dict['meta'])
|
||||||
|
);
|
||||||
|
$response_code = $resp_dict['meta']['code'];
|
||||||
|
// Success
|
||||||
|
if ($response_code >= 200 && $response_code <= 208) {
|
||||||
|
return $resp_dict;
|
||||||
|
} else {
|
||||||
|
write_pca_log(
|
||||||
|
'Received error-response from server. ' .
|
||||||
|
json_encode($resp_dict['meta']),
|
||||||
|
'ERROR'
|
||||||
|
);
|
||||||
|
// die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_messages($clubs)
|
||||||
|
{
|
||||||
|
$num_unread_endpoint =
|
||||||
|
self::$api_endpoint . '/users/me/channels/num_unread/pm';
|
||||||
|
$num_unread_pms = $this->get_data($num_unread_endpoint)['data'];
|
||||||
|
$messages = []; //Keys: username, values: message-array of messages by the user
|
||||||
|
write_pca_log($num_unread_pms . ' unread PMs');
|
||||||
|
if ($num_unread_pms > 0) {
|
||||||
|
$channels_endpoint = self::$api_endpoint .
|
||||||
|
'/users/me/channels/subscribed?include_read=0&channel_types=io.pnut.core.pm';
|
||||||
|
$channels = $this->get_data($channels_endpoint);
|
||||||
|
foreach ($channels['data'] as $channel) {
|
||||||
|
write_pca_log('Channel: ' . $channel['id'], 'DEBUG');
|
||||||
|
$messages_endpoint =
|
||||||
|
self::$api_endpoint .
|
||||||
|
'/channels/' .
|
||||||
|
$channel['id'] .
|
||||||
|
'/messages?include_deleted=0&include_html=0&include_client=0&include_marker=1';
|
||||||
|
$unread_messages = $this->get_data($messages_endpoint);
|
||||||
|
$msg = [];
|
||||||
|
$sender = '';
|
||||||
|
$last_read = $unread_messages['meta']['marker']['last_read_id'];
|
||||||
|
foreach ($unread_messages['data'] as $message) {
|
||||||
|
if ($message['id'] <= $last_read) {
|
||||||
|
//Stop when reaching already read messages
|
||||||
|
write_pca_log(
|
||||||
|
'Message ' .
|
||||||
|
$message['id'] .
|
||||||
|
'is <= last read message ' .
|
||||||
|
$last_read .
|
||||||
|
'. Skipping rest of the messages.',
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$sender_tmp = $message['user']['username'];
|
||||||
|
if (substr($this->me->user_name, 1) == $sender_tmp) {
|
||||||
|
//Ignore messages sent by the bot
|
||||||
|
write_pca_log(
|
||||||
|
'Ignoring message ' .
|
||||||
|
$message['id'] .
|
||||||
|
', because it was sent by myself',
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$sender = $sender_tmp;
|
||||||
|
$message_text = $message['content']['text'];
|
||||||
|
$msg[] = $message['content']['text'];
|
||||||
|
write_pca_log(
|
||||||
|
'Message from ' . $sender . ': ' . $message_text,
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($sender != '') {
|
||||||
|
$messages[$sender] = $msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send_message($user, $message)
|
||||||
|
{
|
||||||
|
$send_message_endpoint =
|
||||||
|
self::$api_endpoint . '/channels/pm/messages?update_marker=1';
|
||||||
|
$parameters = ['text' => $message, 'destinations' => ['@' . $user]];
|
||||||
|
$this->get_data(
|
||||||
|
$send_message_endpoint,
|
||||||
|
$parameters,
|
||||||
|
'POST',
|
||||||
|
'application/json',
|
||||||
|
$user == 'hutattedonmyarm'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_notification_text_template($user_id, $text)
|
||||||
|
{
|
||||||
|
// echo json_encode(self::$settings);
|
||||||
|
self::$settings[$user_id] = $text;
|
||||||
|
$this->save_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_notification_text_template($user_id)
|
||||||
|
{
|
||||||
|
$notification_text = API::$default_notification_text;
|
||||||
|
if (array_key_exists($user_id, self::$settings)) {
|
||||||
|
write_pca_log(
|
||||||
|
$user_id .
|
||||||
|
' has custom settings: ' .
|
||||||
|
json_encode(self::$settings[$user_id])
|
||||||
|
);
|
||||||
|
$notification_text = self::$settings[$user_id];
|
||||||
|
} else {
|
||||||
|
write_pca_log($user_id . ' does not have custom settings!');
|
||||||
|
}
|
||||||
|
return $notification_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build_notification_text(
|
||||||
|
$user,
|
||||||
|
$clubs,
|
||||||
|
$current_pca,
|
||||||
|
$next_pca
|
||||||
|
) {
|
||||||
|
$notification_text = $this->get_notification_text_template(
|
||||||
|
$user->user_name
|
||||||
|
);
|
||||||
|
|
||||||
|
$token_values = [
|
||||||
|
$user->user_name,
|
||||||
|
$user->name,
|
||||||
|
$current_pca['pca'],
|
||||||
|
$current_pca['emoji'],
|
||||||
|
$current_pca['post_count'],
|
||||||
|
$next_pca['pca'],
|
||||||
|
$next_pca['emoji'],
|
||||||
|
$next_pca['post_count'],
|
||||||
|
$next_pca['post_count'] - $user->number_posts,
|
||||||
|
];
|
||||||
|
if (array_key_exists($user->user_name, self::$settings)) {
|
||||||
|
write_pca_log(
|
||||||
|
$user->user_name .
|
||||||
|
' has custom settings: ' .
|
||||||
|
json_encode(self::$settings[$user->username])
|
||||||
|
);
|
||||||
|
$notification_text = self::$settings[$user->username];
|
||||||
|
} else {
|
||||||
|
write_pca_log($user->user_name . ' does not have custom settings!');
|
||||||
|
}
|
||||||
|
foreach (self::$notification_tokens as $index => $token) {
|
||||||
|
$notification_text = str_replace(
|
||||||
|
$token,
|
||||||
|
$token_values[$index],
|
||||||
|
$notification_text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $notification_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset_notification_text_for_user($user_id)
|
||||||
|
{
|
||||||
|
if (array_key_exists($user_id, self::$settings)) {
|
||||||
|
unset(self::$settings[$user_id]);
|
||||||
|
$this->save_settings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function save_settings()
|
||||||
|
{
|
||||||
|
file_put_contents(
|
||||||
|
self::$settings_file_location,
|
||||||
|
json_encode(self::$settings)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function write_post($posttext, $reply_to = -1)
|
||||||
|
{
|
||||||
|
$post_endpoint = self::$api_endpoint . '/posts';
|
||||||
|
$txt = mb_strimwidth($posttext, 0, $this->max_posttext_length, '');
|
||||||
|
// $txt = $posttext;
|
||||||
|
$parameters = ['text' => $txt];
|
||||||
|
if ($reply_to != -1) {
|
||||||
|
$parameters['reply_to'] = $reply_to;
|
||||||
|
}
|
||||||
|
write_pca_log(
|
||||||
|
"Writing post: '" . $txt . "' => " . json_encode($parameters)
|
||||||
|
);
|
||||||
|
$this->get_data($post_endpoint, $parameters, 'POST');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_user($user_id)
|
||||||
|
{
|
||||||
|
if ($user_id[0] != '@') {
|
||||||
|
$user_id = '@' . $user_id;
|
||||||
|
}
|
||||||
|
$user_endpoint = self::$api_endpoint . '/users/' . $user_id;
|
||||||
|
return new User($this->get_data($user_endpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_bot_followers()
|
||||||
|
{
|
||||||
|
$before_id = null;
|
||||||
|
$users = [];
|
||||||
|
do {
|
||||||
|
write_pca_log('Getting followers before id: ' . $before_id);
|
||||||
|
$followers_endpoint = self::$api_endpoint . '/users/me/followers';
|
||||||
|
if ($before_id != null) {
|
||||||
|
$followers_endpoint .= '?before_id=' . $before_id;
|
||||||
|
}
|
||||||
|
$followers_data = $this->get_data(
|
||||||
|
$followers_endpoint, [
|
||||||
|
'before_id' => $before_id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$before_id = $followers_data['meta']['min_id'];
|
||||||
|
$users = array_merge(
|
||||||
|
$users,
|
||||||
|
User::get_users_from_api_reponse($followers_data)
|
||||||
|
);
|
||||||
|
} while ($followers_data['meta']['more'] == '1');
|
||||||
|
return $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_query_string($array)
|
||||||
|
{
|
||||||
|
foreach ($array as $k => &$v) {
|
||||||
|
if ($v === true) {
|
||||||
|
$v = '1';
|
||||||
|
} elseif ($v === false) {
|
||||||
|
$v = '0';
|
||||||
|
}
|
||||||
|
unset($v);
|
||||||
|
}
|
||||||
|
return http_build_query($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notify_user($user, $clubs)
|
||||||
|
{
|
||||||
|
// Get current PCA
|
||||||
|
$current_pca_dict = $user->get_highest_pca($clubs);
|
||||||
|
$current_pca = '';
|
||||||
|
if (array_key_exists('pca', $current_pca_dict)) {
|
||||||
|
$current_pca = $current_pca_dict['pca'];
|
||||||
|
} else {
|
||||||
|
// If key doesn't exist, user hasn't reached a club yet
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$last_notification = $user->last_club_notification;
|
||||||
|
$next_pca = $user->get_next_pca($clubs);
|
||||||
|
$next = [
|
||||||
|
'club' => $next_pca,
|
||||||
|
'posts_until' => $next_pca['post_count'] - $user->number_posts
|
||||||
|
];
|
||||||
|
if ($current_pca != $last_notification) {
|
||||||
|
// Haven't notified about the current club yet
|
||||||
|
// Stitch together the post itself
|
||||||
|
$this->build_notification_text(
|
||||||
|
$user,
|
||||||
|
$clubs,
|
||||||
|
$current_pca_dict,
|
||||||
|
$next_pca
|
||||||
|
);
|
||||||
|
$text_components = [];
|
||||||
|
$posttext = 'Congratulations ' .
|
||||||
|
$user->user_name .
|
||||||
|
', you are now a member of #' .
|
||||||
|
preg_replace('/\s+/', '', $current_pca) .
|
||||||
|
' ' .
|
||||||
|
$current_pca_dict['emoji'];
|
||||||
|
$text_components[] = ' ('
|
||||||
|
. $current_pca_dict['post_count']
|
||||||
|
. '+ posts)!';
|
||||||
|
$text_components[] = ' Next: ' .
|
||||||
|
$next_pca['emoji'] .
|
||||||
|
' at ' .
|
||||||
|
$next_pca['post_count'] .
|
||||||
|
' posts';
|
||||||
|
foreach ($text_components as $component) {
|
||||||
|
$available_length = $this->max_posttext_length - strlen($component);
|
||||||
|
if (strlen($posttext) < $available_length) {
|
||||||
|
$posttext .= $component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Steps to write post as a reply to the post which made a user enter a new club:
|
||||||
|
* 1) Get number of user posts (we already have that info)
|
||||||
|
* 2) Subtract pca post count value from number posts. This will give us an offset
|
||||||
|
* 3) Get their posts with ?count=OFFSET&include_html=0&include_counts=0&include_client=0
|
||||||
|
* 4) Check if counts match. If not, get more of their posts with before_id set to the relevant ID
|
||||||
|
* 5) Repeat 4 until we have the correct post
|
||||||
|
* 6) Reply to it
|
||||||
|
*/
|
||||||
|
$pca_offset = $user->number_posts - $current_pca_dict['post_count'] + 1;
|
||||||
|
$before_id = -1;
|
||||||
|
$loop_counts = 10;
|
||||||
|
do {
|
||||||
|
$ep = self::$api_endpoint .
|
||||||
|
'/users/' .
|
||||||
|
$user->user_name .
|
||||||
|
'/posts?count=' .
|
||||||
|
$pca_offset .
|
||||||
|
'&include_html=0&include_counts=0&include_client=0&include_deleted=1';
|
||||||
|
// write_pca_log($ep, "DEBUG");
|
||||||
|
$response = $this->get_data($ep, []);
|
||||||
|
$pca_offset -= count($response['data']);
|
||||||
|
$before_id = $response['meta']['min_id'];
|
||||||
|
write_pca_log(
|
||||||
|
'Received ' . count($response['data']) . ' posts',
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
$loop_counts--;
|
||||||
|
// write_pca_log("Remaining offset: ".$pca_offset);
|
||||||
|
} while ($pca_offset > 0 && $loop_counts > 0);
|
||||||
|
$reply_to = $response['data'][count($response['data']) - 1]['id'];
|
||||||
|
// reached mac loop iterations
|
||||||
|
if (!isset($reply_to) || $reply_to == 0 || $loop_counts <= 0) {
|
||||||
|
$reply_to = -1;
|
||||||
|
}
|
||||||
|
$del_ar = $response['data'][count($response['data']) - 1];
|
||||||
|
$deleted = array_key_exists('is_deleted', $del_ar) &&
|
||||||
|
$del_ar['is_deleted'] == 'true';
|
||||||
|
$log_string = 'Post that made them reach ' .
|
||||||
|
preg_replace('/\s+/', '', $current_pca) .
|
||||||
|
': ' .
|
||||||
|
$reply_to .
|
||||||
|
'. Deleted?: ';
|
||||||
|
$log_string .= $deleted ? 'yes' : 'no';
|
||||||
|
write_pca_log($log_string);
|
||||||
|
$this->write_post($posttext, $reply_to);
|
||||||
|
$user->last_club_notification = $current_pca;
|
||||||
|
write_pca_log($posttext);
|
||||||
|
write_pca_log($log_string);
|
||||||
|
$now = \DateTime::createFromFormat('U.u', microtime(true));
|
||||||
|
$recent_changes_dict = [
|
||||||
|
'date' => $response['data'][count($response['data']) - 1]['created_at'],
|
||||||
|
'user' => $user->user_name,
|
||||||
|
'pca' => $current_pca,
|
||||||
|
'post_id' => $reply_to,
|
||||||
|
];
|
||||||
|
$history_file = 'history.json';
|
||||||
|
$inp = file_get_contents($history_file);
|
||||||
|
$tempArray = json_decode($inp, true);
|
||||||
|
if ($tempArray == null) {
|
||||||
|
$tempArray = [];
|
||||||
|
}
|
||||||
|
array_push($tempArray, $recent_changes_dict);
|
||||||
|
file_put_contents($history_file, json_encode($tempArray));
|
||||||
|
} else {
|
||||||
|
// Already notified, nothing to do
|
||||||
|
write_pca_log(
|
||||||
|
$user->user_name .
|
||||||
|
' has already been notified for reaching ' .
|
||||||
|
$current_pca
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get all clubs
|
||||||
|
write_pca_log('');
|
||||||
|
write_pca_log('');
|
||||||
|
write_pca_log('');
|
||||||
|
write_pca_log('');
|
||||||
|
write_pca_log('Log file: ' . get_log_file());
|
||||||
|
$clubs = json_decode(file_get_contents('https://pca.phlaym.net/pca.php'), true);
|
||||||
|
write_pca_log('Found clubs: ' . implode(', ', array_column($clubs, 'pca')));
|
||||||
|
// $last_notification_file = '../last_notification.json';
|
||||||
|
$last_notification_file_dir = realpath(__DIR__ . '/../pca_settings');
|
||||||
|
$last_notification_file_name = '/last_notification.json';
|
||||||
|
$last_notification_file = $last_notification_file_dir
|
||||||
|
. $last_notification_file_name;
|
||||||
|
write_pca_log('Last notification file: ' . $last_notification_file);
|
||||||
|
$last_notification_dict = [];
|
||||||
|
|
||||||
|
if (file_exists($last_notification_file)) {
|
||||||
|
$last_notification_dict = json_decode(
|
||||||
|
file_get_contents($last_notification_file),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
write_pca_log('Loaded last notification info from file');
|
||||||
|
} else {
|
||||||
|
write_pca_log('Last notification info file could not be found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get users
|
||||||
|
$access_token = '-1';
|
||||||
|
$token_file = realpath(__DIR__ . '/../pca_settings/access_token');
|
||||||
|
if ($token_file !== false) {
|
||||||
|
$access_token = file_get_contents($token_file);
|
||||||
|
write_pca_log('Using saved authentication');
|
||||||
|
if ($access_token == false) {
|
||||||
|
write_pca_log('Re-authenticating');
|
||||||
|
header('Location: http://pca.phlaym.net/pnutauth.php');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write_pca_log('Re-authenticating');
|
||||||
|
header('Location: http://pca.phlaym.net/pnutauth.php');
|
||||||
|
}
|
||||||
|
$api = new API();
|
||||||
|
$api->access_token = $access_token;
|
||||||
|
$api->init();
|
||||||
|
$messages = $api->get_messages($clubs);
|
||||||
|
/*
|
||||||
|
* Message command syntax:
|
||||||
|
* "Help" or "?" => prints help
|
||||||
|
* "Get notification text" => replies with notification text
|
||||||
|
* "Reset notification text" => resets to default
|
||||||
|
* "Set notification text" followed by a space and then the text. Replies with sample text. Available tokens see class var
|
||||||
|
*/
|
||||||
|
foreach ($messages as $sender => $message) {
|
||||||
|
$sender_obj = $api->get_user($sender);
|
||||||
|
foreach ($message as $msg) {
|
||||||
|
if (mb_strtolower($msg) == '?' || mb_strtolower($msg) == 'help') {
|
||||||
|
$helptext = 'Welcome to ' . $api->me->user_name . "!\n";
|
||||||
|
$helptext .= "The following commands are available: \n";
|
||||||
|
$helptext .=
|
||||||
|
"- 'Get notification text', which prints the current notification text for you\n";
|
||||||
|
$helptext .=
|
||||||
|
"- 'Set notification text', followed by a space and a custom notification text\n";
|
||||||
|
$helptext .=
|
||||||
|
"- 'Reset notification text', resets the notification text to the default\n";
|
||||||
|
$helptext .= "\n";
|
||||||
|
$helptext .= " Available tokens for the custom notification text\n";
|
||||||
|
$helptext .= " - '{user.username}', your username (including the @)\n";
|
||||||
|
$helptext .= " - '{user.name}', your real name\n";
|
||||||
|
$helptext .=
|
||||||
|
" - '{pca.name}', the name of the PCA you achieved, excluding the #\n";
|
||||||
|
$helptext .= " - '{pca.emoji}', the emoji of the PCA you achieved\n";
|
||||||
|
$helptext .=
|
||||||
|
" - '{pca.postcount}', the number of posts you need to achieve the PCA\n";
|
||||||
|
$helptext .= " - '{nextpca.name}', the name of the next PCA\n";
|
||||||
|
$helptext .= " - '{nextpca.emoji}', the emoji of the next PCA\n";
|
||||||
|
$helptext .=
|
||||||
|
" - '{nextpca.postcount}', the total number of posts needed for the next PCA\n";
|
||||||
|
$helptext .=
|
||||||
|
" - '{posts_to_pca}', the number of posts missing to the next PCA\n";
|
||||||
|
$api->send_message($sender, $helptext);
|
||||||
|
//Print help text
|
||||||
|
} elseif (substr(mb_strtolower($msg), 0, 21) === 'get notification text') {
|
||||||
|
//Print current notification text for user
|
||||||
|
//TODO: build example notification text. Create user object and do 'get_highest_pca($clubs)' and 'get_next_pca($clubs)'
|
||||||
|
$api->send_message(
|
||||||
|
$sender,
|
||||||
|
"You current notification text is: \n" .
|
||||||
|
$api->get_notification_text_template($sender)
|
||||||
|
);
|
||||||
|
} elseif (substr(mb_strtolower($msg), 0, 23) === 'reset notification text'
|
||||||
|
) {
|
||||||
|
//reset notification text for user to default
|
||||||
|
$api->reset_notification_text_for_user($sender);
|
||||||
|
$api->send_message(
|
||||||
|
$sender,
|
||||||
|
"Your notification text has been reset back to the default: \n" .
|
||||||
|
$api->get_notification_text_template($sender)
|
||||||
|
);
|
||||||
|
} elseif (substr(mb_strtolower($msg), 0, 21) === 'set notification text') {
|
||||||
|
$notification_text = substr($msg, 22);
|
||||||
|
$api->set_notification_text_template($sender, $notification_text);
|
||||||
|
$api->send_message(
|
||||||
|
$sender,
|
||||||
|
"Your notification text has been set to:\n" .
|
||||||
|
$api->get_notification_text_template($sender)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
write_pca_log(
|
||||||
|
"Unknown command: '"
|
||||||
|
. $msg
|
||||||
|
. "' from "
|
||||||
|
. $sender,
|
||||||
|
'DEBUG'
|
||||||
|
);
|
||||||
|
$api->send_message(
|
||||||
|
$sender,
|
||||||
|
"I'm sorry, I don't recognize that. Try PMing me 'help'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$followers = $api->get_bot_followers();
|
||||||
|
// Check and notify. Also build notification dict
|
||||||
|
$next_club_infos = [];
|
||||||
|
foreach ($followers as $user) {
|
||||||
|
if ($last_notification_dict != null
|
||||||
|
&& array_key_exists($user->user_name, $last_notification_dict)
|
||||||
|
) {
|
||||||
|
write_pca_log(
|
||||||
|
$user->user_name .
|
||||||
|
' has been notified in the past. Last time when reaching: ' .
|
||||||
|
$last_notification_dict[$user->user_name]
|
||||||
|
);
|
||||||
|
$user->last_club_notification = $last_notification_dict[$user->user_name];
|
||||||
|
} else {
|
||||||
|
write_pca_log($user->user_name . ' has never been notified in the past');
|
||||||
|
}
|
||||||
|
$next_club_info = $api->notify_user($user, $clubs);
|
||||||
|
if ($next_club_info !== null) {
|
||||||
|
$next_club_info['user'] = $user->user_name;
|
||||||
|
$next_club_infos[] = $next_club_info;
|
||||||
|
}
|
||||||
|
if ($user->last_club_notification != '') {
|
||||||
|
write_pca_log(
|
||||||
|
'Saving last notification ' .
|
||||||
|
$user->last_club_notification .
|
||||||
|
' for user ' .
|
||||||
|
$user->user_name
|
||||||
|
);
|
||||||
|
$last_notification_dict[$user->user_name] = $user->last_club_notification;
|
||||||
|
} else {
|
||||||
|
write_pca_log(
|
||||||
|
$user->user_name .
|
||||||
|
' will not be saved, as they have not reached a club yet'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usort(
|
||||||
|
$next_club_infos,
|
||||||
|
fn($a, $b) => intval($a['posts_until']) - intval($b['posts_until'])
|
||||||
|
);
|
||||||
|
foreach ($next_club_infos as $idx => $value) {
|
||||||
|
if ($idx >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write_pca_log(
|
||||||
|
'Next club will be: '
|
||||||
|
. $value['club']['pca']
|
||||||
|
. ' for user '
|
||||||
|
. $value['user']
|
||||||
|
. ' in '
|
||||||
|
. $value['posts_until']
|
||||||
|
. ' posts'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save changes
|
||||||
|
write_pca_log('Saving updated notification info');
|
||||||
|
$f = fopen($last_notification_file, 'w');
|
||||||
|
fwrite($f, json_encode($last_notification_dict));
|
||||||
|
fclose($f);
|
||||||
|
|
||||||
|
?>
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
A small [https://pnut.io](https://pnut.io) bot.
|
||||||
|
|
||||||
|
Can be pretty much cloned and run as-is,
|
||||||
|
with the only exception being a file called clientsecret in `../pca_settings/`, containing the client secret (duh).
|
||||||
|
If you don't have one, get it [here](https://pnut.io/dev)
|
||||||
|
|
||||||
|
The bot is currently called roughly every 15 minutes by a cronjob,
|
||||||
|
so any follows/new club memberships might take a while to show up.
|
||||||
|
|
||||||
|
Used to live at https://wedro.online/Check_PCA.php,
|
||||||
|
now lives at https://pca.phlaym.net/Check_PCA.php.
|
||||||
|
|
||||||
|
Source used to be [on Github](https://github.com/hutattedonmyarm/pcabot),
|
||||||
|
but has now moved to [my Gitea instance](https://git.phlaym.net/phlaym/pca).
|
||||||
|
Without its history, mostly because the old repo contains a various assortment of Pnut related
|
||||||
|
stuff and this is much more split up now.
|
||||||
|
|
||||||
|
Required PHP 8.x, tested with >=8.4
|
1
pca.json
Normal file
1
pca.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"pca":"RollClub","emoji":"\ud83c\udf5e","post_count":"500","inventor":"@mlv"},{"pca":"MetalClub","emoji":"\ud83e\udd18","post_count":"666","inventor":"@hutattedonmyarm"},{"pca":"CrumpetClub","emoji":"\ud83c\udf70","post_count":"1000","inventor":"@irina"},{"pca":"NoonClub","emoji":"\ud83d\udd5b","post_count":"1200","inventor":"@samweinberg"},{"pca":"EnterpriseClub","emoji":"\ud83d\ude80","post_count":"1701","inventor":"@thedoctor,@ludolphus,@wife"},{"pca":"BitesizeCookieClub","emoji":"\ud83c\udf65","post_count":"2000","inventor":"@saket"},{"pca":"PnutClub","emoji":"\ud83e\udd5c","post_count":"2016","inventor":"@hutattedonmyarm"},{"pca":"CrunchClub","emoji":"\u260e\ufe0f","post_count":"2600","inventor":"@infodriveway"},{"pca":"MysteryScienceClub","emoji":"\ud83d\udce1","post_count":"3000","inventor":"@blt"},{"pca":"LDRClub","emoji":"\ud83d\udc5f","post_count":"5000","inventor":"@kym"},{"pca":"CPCClub","emoji":"\u2328\ufe0f","post_count":"6128","inventor":"@papierzeit"},{"pca":"IBMPCClub","emoji":"\ud83d\udcbb","post_count":"8088","inventor":"@duerig"},{"pca":"CookieClub","emoji":"\ud83c\udf6a","post_count":"10000","inventor":"@bondman"},{"pca":"SpinalTapClub","emoji":"\ud83d\udc89","post_count":"11000","inventor":"@paulyhedral"},{"pca":"BreakfastClub","emoji":"\ud83c\udf73","post_count":"20000","inventor":"@trine"},{"pca":"CaratClub","emoji":"\ud83d\udc8e","post_count":"24000","inventor":"@saket"},{"pca":"PeshawarClub","emoji":"\ud83c\udf5b","post_count":"25000","inventor":"@peemee"},{"pca":"MileHighClub","emoji":"\u2708\ufe0f","post_count":"30000","inventor":"@alicia"},{"pca":"PiClub","emoji":"\u2b55\ufe0f","post_count":"31416","inventor":"@kdfrawg"},{"pca":"TowelClub","emoji":"\ud83d\udc33","post_count":"42000","inventor":"@mps"},{"pca":"HitmanClub","emoji":"\ud83d\udd2a","post_count":"47000","inventor":"@remus"},{"pca":"BaconClub","emoji":"\ud83d\udc37","post_count":"50000","inventor":"@orangesn0w"},{"pca":"BrowncoatClub","emoji":"\ud83d\ude80","post_count":"57000","inventor":"@thedoctor"},{"pca":"CommodoreClub","emoji":"\ud83d\udd31","post_count":"64000","inventor":"@saket"},{"pca":"MotorolaClub","emoji":"\u24c2\ufe0f","post_count":"68000","inventor":"@kohlmannj"},{"pca":"TromboneClub","emoji":"\ud83c\udfb6","post_count":"76000","inventor":"@charlesg"},{"pca":"WiFiClub","emoji":"\ud83d\udcf6","post_count":"80211","inventor":"@rdo"},{"pca":"PajamaClub","emoji":"\ud83d\udcb7","post_count":"90000","inventor":"@mps"},{"pca":"TowerOfBabble","emoji":"\ud83d\uddfc","post_count":"100000","inventor":"@sham"},{"pca":"MacClub","emoji":"\ud83d\udcbb","post_count":"128000","inventor":"@fields"},{"pca":"TwitterLeaverClub","emoji":"","post_count":"140000","inventor":"@nhat"},{"pca":"GetALifeNoSrslyClub","emoji":"\ud83d\udc40","post_count":"200000","inventor":"@saket"},{"pca":"MeaninglessPostCountClub","emoji":"\ud83d\udcaf","post_count":"231568","inventor":"@cunarders"},{"pca":"ADNClub","emoji":"","post_count":"256000","inventor":"@trine"},{"pca":"PensionersClub","emoji":"\ud83c\udf97","post_count":"401000","inventor":"@sham"},{"pca":"MaglevClub","emoji":"\ud83d\ude84","post_count":"430000","inventor":"@remus"},{"pca":"LaughterClub","emoji":"\ud83e\udd23","post_count":"555000","inventor":"@saket"},{"pca":"GatesClub","emoji":"\ud83d\udcac","post_count":"640000","inventor":"@adiabatic"},{"pca":"JoyLuckClub","emoji":"\ud83c\udfa2","post_count":"888000","inventor":"@alicia"},{"pca":"MillionairesClub","emoji":"\ud83d\udcb0","post_count":"1000000","inventor":"@mps"},{"pca":"ToTheMoonClub","emoji":"\ud83d\udcc8","post_count":"21000000","inventor":"@blumenkraft"},{"pca":"GoogolplexClub","emoji":"\u03a9","post_count":"10000\u00ad000\u00ad\u00ad000000000\u00ad\u00ad000\u00ad000\u00ad000\u00ad\u00ad000\u00ad000\u00ad000\u00ad000\u00ad000\u00ad000\u00ad\u00ad000\u00ad000\u00ad000\u00ad\u00ad000\u00ad000000\u00ad\u00ad000\u00ad000\u00ad000\u00ad000\u00ad000\u00ad000\u00ad\u00ad000000\u00ad000\u00ad\u00ad000\u00ad000\u00ad000\u00ad\u00ad000","inventor":"@saket"}]
|
37
pca.php
Normal file
37
pca.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
$filename = 'pca.json';
|
||||||
|
if (file_exists($filename) && (time()-720 < filemtime($filename))) {
|
||||||
|
echo file_get_contents($filename);
|
||||||
|
} else {
|
||||||
|
output_pca($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
function output_pca($filename) {
|
||||||
|
$html = file_get_contents('https://wiki.pnut.io/PCA');
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
$doc->loadHTML($html);
|
||||||
|
$tables = $doc->getElementsByTagName('table');
|
||||||
|
|
||||||
|
$pca = array();
|
||||||
|
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
if ($table->hasAttribute('class') && $table->getAttribute('class') == 'wikitable') {
|
||||||
|
foreach ($table->getElementsByTagName('tr') as $childNode) {
|
||||||
|
$entry = $childNode->getElementsByTagName('td');
|
||||||
|
if ($entry->length > 0) {
|
||||||
|
$achievement["pca"] = preg_replace('/\s+/', '', $entry->item(0)->textContent);
|
||||||
|
$achievement["emoji"] = preg_replace('/\s+/', '', $entry->item(1)->textContent);
|
||||||
|
$achievement["post_count"] = preg_replace('/\s+/', '', $entry->item(2)->textContent);
|
||||||
|
$achievement["inventor"] = preg_replace('/\s+/', '', $entry->item(3)->textContent);
|
||||||
|
$pca[] = $achievement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$myfile = fopen($filename, "w");
|
||||||
|
$file_content = json_encode($pca);
|
||||||
|
fwrite($myfile, $file_content);
|
||||||
|
fclose($myfile);
|
||||||
|
echo $file_content;
|
||||||
|
}
|
||||||
|
?>
|
39
pnutauth.php
Normal file
39
pnutauth.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
$redirect_uri = 'https://pca.phlaym.net/pnutauth.php';
|
||||||
|
$client_id = 'RwHDh73PtU0It4DdKhwh2GEagBoO1ELD';
|
||||||
|
$client_secret = file_get_contents('../pca_settings/clientsecret');
|
||||||
|
if (isset($_GET['code'])) {
|
||||||
|
$code = $_GET['code'];
|
||||||
|
|
||||||
|
// set post fields
|
||||||
|
$post = [
|
||||||
|
'client_id' => $client_id,
|
||||||
|
'client_secret' => $client_secret,
|
||||||
|
'code' => $code,
|
||||||
|
'redirect_uri' => $redirect_uri,
|
||||||
|
'grant_type' => 'authorization_code'
|
||||||
|
];
|
||||||
|
|
||||||
|
$ch = curl_init('https://api.pnut.io/v1/oauth/access_token');
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||||
|
|
||||||
|
// execute!
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// close the connection, release resources used
|
||||||
|
curl_close($ch);
|
||||||
|
$resp = json_decode($response, true);
|
||||||
|
|
||||||
|
file_put_contents('../pca_settings/access_token', $resp['access_token']);
|
||||||
|
header('Location: https://pca.phlaym.net/Check_PCA.php');
|
||||||
|
} else {
|
||||||
|
header(
|
||||||
|
'Location: https://pnut.io/oauth/authenticate?client_id='
|
||||||
|
. $client_id
|
||||||
|
. '&redirect_uri='
|
||||||
|
. urlencode($redirect_uri)
|
||||||
|
. '&scope=write_post,messages&response_type=code'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
204
recent_progressions.php
Normal file
204
recent_progressions.php
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Pnut.io PCA progression history</title>
|
||||||
|
<link rel="stylesheet" href="styles/style_index.css" />
|
||||||
|
<script>
|
||||||
|
var startRow;
|
||||||
|
var tableID = "progressionTable"
|
||||||
|
var filterStyles = ["str", "<=", ">="];
|
||||||
|
var filterStyleIdx = 0;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var numTH = document.getElementsByTagName('th').length;
|
||||||
|
startRow = (document.getElementsByTagName('tr').length * numTH) / (document.getElementsByTagName('td').length + numTH);
|
||||||
|
//Make filter row visible. (Hidden by default, so users with JS disabled don't see it)
|
||||||
|
var sr = document.getElementById('searchRow').style.display = "";
|
||||||
|
var datalist = document.getElementById('filterPCA');
|
||||||
|
if ('options' in datalist) {
|
||||||
|
|
||||||
|
xmlhttp=new XMLHttpRequest();
|
||||||
|
xmlhttp.onreadystatechange=function() {
|
||||||
|
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
|
||||||
|
var pcaDict = JSON.parse(xmlhttp.responseText);
|
||||||
|
var options = '';
|
||||||
|
pcaDict.forEach(function(element) {
|
||||||
|
options += '<option value="' + element.pca + '" />';
|
||||||
|
});
|
||||||
|
datalist.innerHTML = options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xmlhttp.open("GET","pca.json");
|
||||||
|
xmlhttp.send();
|
||||||
|
}
|
||||||
|
sortTable(3, "desc");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDate(d) {
|
||||||
|
return (d !== "Invalid Date") && !isNaN(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeIdFilterStyle(sender) {
|
||||||
|
filterStyleIdx++;
|
||||||
|
if (filterStyleIdx >= filterStyles.length) {
|
||||||
|
filterStyleIdx = 0;
|
||||||
|
}
|
||||||
|
sender.textContent = filterStyles[filterStyleIdx];
|
||||||
|
if (document.getElementById('filterPost').value != "") {
|
||||||
|
filter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortTable(n, dir) {
|
||||||
|
var rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||||
|
var table = document.getElementById(tableID);
|
||||||
|
switching = true;
|
||||||
|
dir = dir || "asc";
|
||||||
|
while (switching) {
|
||||||
|
switching = false;
|
||||||
|
rows = table.getElementsByTagName("tr");
|
||||||
|
for (i = startRow; i < (rows.length - 1); i++) {
|
||||||
|
shouldSwitch = false;
|
||||||
|
x = rows[i].getElementsByTagName("td")[n];
|
||||||
|
y = rows[i + 1].getElementsByTagName("td")[n];
|
||||||
|
if (dir == "asc") {
|
||||||
|
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
|
||||||
|
shouldSwitch= true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (dir == "desc") {
|
||||||
|
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
|
||||||
|
shouldSwitch= true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldSwitch) {
|
||||||
|
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
||||||
|
if (switchcount == 0) {
|
||||||
|
var headers = Array.from(rows[0].getElementsByTagName("th"));
|
||||||
|
headers.forEach(function(element) {
|
||||||
|
element.innerHTML = element.innerHTML.replace('▲', '');
|
||||||
|
element.innerHTML = element.innerHTML.replace('▼', '');
|
||||||
|
});
|
||||||
|
headers[n].innerHTML += dir == 'asc' ? '▲' : '▼';
|
||||||
|
}
|
||||||
|
switching = true;
|
||||||
|
switchcount ++;
|
||||||
|
} else {
|
||||||
|
if (switchcount == 0 && dir == "asc") {
|
||||||
|
dir = "desc";
|
||||||
|
switching = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter() {
|
||||||
|
var table = document.getElementById(tableID);
|
||||||
|
//Get values to filter for
|
||||||
|
var filterUser = document.getElementById('filterUser').value.toUpperCase();
|
||||||
|
var filterPCA = document.getElementById('filterPCAinput').value.toUpperCase();
|
||||||
|
var filterPost = document.getElementById('filterPost').value;
|
||||||
|
var filterDateBefore = document.getElementById('filterDateBefore').value;
|
||||||
|
var filterDateAfter = document.getElementById('filterDateAfter').value;
|
||||||
|
|
||||||
|
//Only filter if entered dates are valid
|
||||||
|
var filterBefore = new Date(filterDateBefore);
|
||||||
|
var filterAfter = new Date(filterDateAfter);
|
||||||
|
var doFilterBefore = isDate(filterBefore);
|
||||||
|
var doFilterAfter = isDate(filterAfter);
|
||||||
|
|
||||||
|
var filterArray = [filterUser, filterPCA, filterPost];
|
||||||
|
var tr = table.getElementsByTagName("tr");
|
||||||
|
var tableData, i, j, userData, showRow, postIdString, postID;
|
||||||
|
var filterStyle = filterStyles[filterStyleIdx];
|
||||||
|
//Loop through every row, skipping the headers
|
||||||
|
for (i = startRow; i < tr.length; i++) {
|
||||||
|
tableData = tr[i].getElementsByTagName("td");
|
||||||
|
showRow = true;
|
||||||
|
postIdString = tableData[2].children[0].innerHTML;
|
||||||
|
postID = parseInt(postIdString);
|
||||||
|
//Filter post ID depending on the selected filter style
|
||||||
|
switch(filterStyle) {
|
||||||
|
case "str":
|
||||||
|
showRow = showRow && ((postIdString.toUpperCase().indexOf(filterPost) > -1));
|
||||||
|
break;
|
||||||
|
case "<=":
|
||||||
|
showRow = showRow && !isNaN(postID) && postID <= filterPost;
|
||||||
|
break;
|
||||||
|
case ">=":
|
||||||
|
showRow = showRow && !isNaN(postID) && postID >= filterPost;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//Filter other columns (except date)
|
||||||
|
for (j = 0; j < tableData.length - 2; j++) {
|
||||||
|
showRow = showRow && ((tableData[j].innerHTML.toUpperCase().indexOf(filterArray[j]) > -1));
|
||||||
|
}
|
||||||
|
//Filter date, depending on the presence and validity of the entered filter date
|
||||||
|
var postDate = new Date(tableData[tableData.length - 1].innerHTML.split(' ')[0]);
|
||||||
|
var doFilterDate = doFilterBefore || doFilterAfter;
|
||||||
|
var filterBeforeHit = !doFilterBefore || (doFilterBefore && postDate <= filterBefore)
|
||||||
|
var filterAfterHit = !doFilterAfter || (doFilterAfter && postDate >= filterAfter)
|
||||||
|
//Show/hide row
|
||||||
|
showRow = showRow && (!doFilterDate || (doFilterDate && filterBeforeHit && filterAfterHit));
|
||||||
|
tr[i].style.display = showRow ? "" : "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="init()">
|
||||||
|
<p>Recent PCA progressions:</p>
|
||||||
|
<?php
|
||||||
|
$history_file = 'history.json';
|
||||||
|
if (!file_exists($history_file)) {
|
||||||
|
echo "None :( </body></html>";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
$recent_changes = json_decode(file_get_contents($history_file), true);
|
||||||
|
if (count($recent_changes) == 0) {
|
||||||
|
echo "None :( </body></html>";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<table id="progressionTable">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th onclick="sortTable(0)">User</th>
|
||||||
|
<th onclick="sortTable(1)">PCA</th>
|
||||||
|
<th onclick="sortTable(2)">Post ID</th>
|
||||||
|
<th onclick="sortTable(3)">Date</th>
|
||||||
|
</tr>
|
||||||
|
<tr id="searchRow" style="display: none;">
|
||||||
|
<th><input type="text" id="filterUser" onpaste="filter()" onkeyup="filter()" placeholder="Filter User"></th>
|
||||||
|
<th>
|
||||||
|
<input list="filterPCA" id="filterPCAinput" onchange="filter()" placeholder="Filter PCA">
|
||||||
|
<datalist id="filterPCA">
|
||||||
|
<option value="test"></option>
|
||||||
|
</datalist>
|
||||||
|
<!-- <input type="text" id="filterPCA" onpaste="filter()" onkeyup="filter()" placeholder="Filter PCA"> !-->
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<button onclick="changeIdFilterStyle(this)" title="Filter post ID by
str: String
<= and >=: Numeric">str</button>
|
||||||
|
<input type="text" id="filterPost" onpaste="filter()" onkeyup="filter()" placeholder="Filter Post ID" style="width: 60%">
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<input type="date" id="filterDateBefore" onkeyup="filter()" onchange="filter()" placeholder="Posts on/before" style=" width:45%; margin-right: 1%">
|
||||||
|
<input type="date" id="filterDateAfter" onkeyup="filter()" onchange="filter()" placeholder="Posts on/after" style=" width: 45%; margin-left: 1%">
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<?php
|
||||||
|
foreach ($recent_changes as $entry) {
|
||||||
|
$user = '<a href="https://pnut.io/'.$entry['user'].'">'.$entry['user'].'</a>';
|
||||||
|
$pca = $entry['pca'];
|
||||||
|
$datetime = new DateTime($entry['date']);
|
||||||
|
$date = $datetime->format('Y-m-d H:i:s P');
|
||||||
|
$post_id = '<a href="https://posts.pnut.io/'.$entry['post_id'].'">'.$entry['post_id'].'</a>';
|
||||||
|
echo '<tr><td>'.$user.'</td><td>'.$pca.'</td><td>'.$post_id.'</td><td>'.$date.'</td></tr>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
2
styles/.htaccess
Normal file
2
styles/.htaccess
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
Options All -Indexes
|
36
styles/style_index.css
Normal file
36
styles/style_index.css
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
body {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: "arial";
|
||||||
|
}
|
||||||
|
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.arrow:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
display: block;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
border-bottom: 5px solid transparent;
|
||||||
|
border-top: 5px solid #000;
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user