Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add X limit handling and Logs Export button #903

Merged
merged 28 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e1183f0
feat: add log export btn and twitter limit handler
Soare-Robert-Daniel Jan 3, 2024
2116379
chore: remove testing var
Soare-Robert-Daniel Jan 3, 2024
6c6d721
feat: share post via rop server
Soare-Robert-Daniel Jan 4, 2024
b0ea0e3
feat: block share with image for users who use rop server & update au…
Soare-Robert-Daniel Jan 5, 2024
daf39b0
chore: clean up
Soare-Robert-Daniel Jan 5, 2024
ed291cb
feat: new logs structure display
Soare-Robert-Daniel Jan 5, 2024
3959211
chore: improve logs color scheme
Soare-Robert-Daniel Jan 8, 2024
7249366
chore: remove note
Soare-Robert-Daniel Jan 8, 2024
56a5d88
chore: clean up
Soare-Robert-Daniel Jan 8, 2024
9aef07d
chore: better null checking
Soare-Robert-Daniel Jan 9, 2024
d1000fd
chore: add missing plugin version in URL
Soare-Robert-Daniel Jan 9, 2024
6659011
chore: server response structure
Soare-Robert-Daniel Jan 11, 2024
23277f9
chore: js build
Soare-Robert-Daniel Jan 11, 2024
bb52a3d
chore: use staging env
Soare-Robert-Daniel Jan 15, 2024
ff32c24
chore: response override condition
Soare-Robert-Daniel Jan 16, 2024
13b6319
chore: free as default license key for sharing
Soare-Robert-Daniel Jan 16, 2024
da5851d
fix: align twitter sing-in account button text
Soare-Robert-Daniel Jan 19, 2024
0b9e009
fix: auth accounts counting when services might be empty
Soare-Robert-Daniel Jan 19, 2024
d769b3a
chore: make text uppercase
Soare-Robert-Daniel Jan 19, 2024
67cc1b3
chore: twitter label
Soare-Robert-Daniel Jan 19, 2024
1b67a0b
chore: phpcs
Soare-Robert-Daniel Jan 19, 2024
7917324
fix: array access for stdClass
Soare-Robert-Daniel Jan 26, 2024
6eb3d00
fix: reload the page even if the opener is not available
Soare-Robert-Daniel Jan 26, 2024
705b400
fix: toggle active account alignment
Soare-Robert-Daniel Jan 26, 2024
07f6e76
chore: default error message
Soare-Robert-Daniel Jan 31, 2024
0066842
fix: set api version before making the tweet
Soare-Robert-Daniel Jan 31, 2024
a6f593c
fix: headers extraction rom RoP server
Soare-Robert-Daniel Jan 31, 2024
7a35cc2
chore: switch to production
Soare-Robert-Daniel Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion assets/css/rop_core.css
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,10 @@ a.active {
border-left-color: #fff;
}

#rop_core .btn.btn-twitter svg {
margin-bottom: -2px;
}

#rop_core .btn.btn-tumblr {
border-color: #304d69;
color: #fff;
Expand Down Expand Up @@ -974,7 +978,7 @@ a.active {
}

#rop_core .form-group:not(:last-child) {
margin-bottom: 0.4rem;
margin-bottom: 0.8rem;
}

#rop_core fieldset {
Expand Down Expand Up @@ -4826,3 +4830,7 @@ padding: 0 20px;
color: #fff;
text-decoration: underline;
}

#rop_core .uppercase {
text-transform: uppercase;
}
2 changes: 1 addition & 1 deletion assets/js/build/dashboard.js

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion includes/admin/class-rop-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,16 @@ public function enqueue_scripts() {
$added_services = $services->get_authenticated_services();
$added_networks = 0;
if ( $added_services ) {
$added_networks = count( array_unique( wp_list_pluck( array_values( $added_services ), 'service' ) ) );

$uniq_auth_accounts = array();

foreach ( $added_services as $key => $service ) {
if ( isset( $service['service'] ) && ! in_array( $service['service'], $uniq_auth_accounts, true ) ) {
$uniq_auth_accounts[] = $service['service'];
}
}

$added_networks = count( $uniq_auth_accounts );
}

$global_settings = new Rop_Global_Settings();
Expand Down Expand Up @@ -370,6 +379,7 @@ public function enqueue_scripts() {
'authToken' => $token,
'adminUrl' => urlencode( $admin_url ),
'authSignature' => $signature,
'pluginVersion' => ROP_LITE_VERSION,
);

if ( 'publish_now' === $page ) {
Expand Down
8 changes: 7 additions & 1 deletion includes/admin/class-rop-rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,8 @@ private function remove_service( $data ) {
/**
* API method called to retrieve a service sign in url.
*
* Used to create an authentication url for users who want to use their own app via personal auth keys/tokens.
*
* @SuppressWarnings(PHPMD.UnusedPrivateMethod) As it is called dynamically.
* @Throws Exception Throws an exception if the service can't be built.
*
Expand Down Expand Up @@ -1060,7 +1062,11 @@ private function add_account_tw( $data ) {
$model = new Rop_Services_Model();
$db = new Rop_Db_Upgrade();

$twitter_service->add_account_with_app( $data );
if ( ! empty( $data['pages'] ) && ! empty( $data['pages']['credentials']['rop_auth_token'] ) ) {
$twitter_service->add_account_from_rop_server( $data );
} else {
$twitter_service->add_account_with_app( $data );
}

$services[ $twitter_service->get_service_id() ] = $twitter_service->get_service();
$active_accounts = array_merge( $active_accounts, $twitter_service->get_service_active_accounts() );
Expand Down
238 changes: 210 additions & 28 deletions includes/admin/services/class-rop-twitter-service.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public function authorize() {
* @param string $oauth_token The OAuth Token. Default empty.
* @param string $oauth_token_secret The OAuth Token Secret. Default empty.
*
* @return mixed
* @return \Abraham\TwitterOAuth\TwitterOAuth
*/
public function get_api( $oauth_token = '', $oauth_token_secret = '' ) {
if ( $this->api == null ) {
Expand Down Expand Up @@ -288,6 +288,8 @@ public function set_credentials( $args ) {
private function get_users( $data = null ) {
// assign default values to variable
$user = $this->user_default;

// Check credentials if the user is using his own dev account.
if ( $data == null ) {
$this->set_api( $this->credentials['oauth_token'], $this->credentials['oauth_token_secret'], $this->consumer_key, $this->consumer_secret );
$api = $this->get_api();
Expand Down Expand Up @@ -573,29 +575,27 @@ public function share( $post_details, $args = array() ) {
return false;
}

$is_rop_app = get_option( 'rop_twitter_via_rs_app', 'no' );
if ( 'yes' === $is_rop_app ) {
$check_sharing_limit = Rop_Admin::rop_check_reached_sharing_limit( 'tw' );
if ( $check_sharing_limit && ! $check_sharing_limit->is_valid_license ) {
$this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', Rop_I18n::get_labels( 'sharing.invalid_license' ) ) );
return false;
}
$share_via_rop_server = ! empty( $this->credentials['rop_auth_token'] );

if ( $check_sharing_limit && ! $check_sharing_limit->is_valid ) {
$error_message = sprintf( Rop_I18n::get_labels( 'sharing.reached_sharing_limit' ), $check_sharing_limit->limit );
$this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', $error_message ) );
return false;
}
$transient_key = 'rop_twitter_limit_reset_' . wp_hash( $share_via_rop_server ? $this->credentials['rop_auth_token'] : $this->credentials['oauth_token'] );
$limit_saved_msg = get_transient( $transient_key );

if ( ! empty( $limit_saved_msg ) ) {
$this->logger->alert_error( $limit_saved_msg );
return false;
}

$this->set_api(
$this->credentials['oauth_token'],
$this->credentials['oauth_token_secret'],
isset( $this->credentials['consumer_key'] ) ? $this->credentials['consumer_key'] : '',
isset( $this->credentials['consumer_secret'] ) ? $this->credentials['consumer_secret'] : ''
);
$api = $this->get_api();
$new_post = array();
$api = null;
if ( ! $share_via_rop_server ) {
$this->set_api(
$this->credentials['oauth_token'],
$this->credentials['oauth_token_secret'],
isset( $this->credentials['consumer_key'] ) ? $this->credentials['consumer_key'] : '',
isset( $this->credentials['consumer_secret'] ) ? $this->credentials['consumer_secret'] : ''
);
$api = $this->get_api();
}

$post_id = $post_details['post_id'];
$post_url = $post_details['post_url'];
Expand All @@ -612,8 +612,10 @@ public function share( $post_details, $args = array() ) {
}

// Twitter media post
if ( ! empty( $share_as_image_post ) || get_post_type( $post_id ) === 'attachment' ) {
if ( isset( $api ) && ! empty( $share_as_image_post ) || get_post_type( $post_id ) === 'attachment' ) {
$new_post = $this->twitter_media_post( $post_details, $api );
} elseif ( ! isset( $api ) && ! empty( $share_as_image_post ) ) {
$this->logger->info( __( 'Post with image is available only the local mode (Use my own API Keys). You can find the option when adding your X account to the plugin Dashboard.', 'tweet-old-post' ) . ' ' . __( ' Read more on:', 'tweet-old-post' ) . 'https://docs.revive.social/article/1908-how-to-solve-453-twitter-error-in-rop' );
}

if ( empty( $new_post ) ) {
Expand All @@ -634,10 +636,97 @@ public function share( $post_details, $args = array() ) {

$this->logger->info( sprintf( 'Before twitter share: %s', json_encode( $new_post ) ) );

$api->setApiVersion( '2' );
$response = $api->post( 'tweets', $new_post, true );
$response = array();
$response_headers = array();
$server_response = array();

if ( ! $share_via_rop_server ) {
$api->setApiVersion( '2' ); // Note: Make sure to always set the correct API version before making a request.
$response = $api->post( 'tweets', $new_post, true );
$response_headers = $api->getLastXHeaders();

if ( isset( $response->data->id ) ) {
$this->logger->info( sprintf( '[X API] Response: %s', json_encode( $response_headers ) ) );

$response = (array) $response;
if ( ! empty( $response['data'] ) ) {
$response['data'] = (array) $response['data'];
}
} else {
$response = $this->rop_share_post_via_server( 'tw', $new_post, $this->credentials['rop_auth_token'] );

$this->logger->info( sprintf( '[Revive Social] Response: %s', json_encode( $response_headers ) ) );

$body = wp_remote_retrieve_body( $response );
$body = json_decode( $body, true );

if ( ! empty( $body ) ) {

if ( ! empty( $body['server'] ) ) {
$server_response = $body['server'];

// If we have a cached response, use it to apply the logic for rate limiting.
if ( ! empty( $server_response['cached_response'] ) ) {
$body = $server_response['cached_response'];
}
}

if ( ! empty( $body['api_headers'] ) ) {
$response_headers = $body['api_headers'];
}

if ( ! empty( $body['api_body'] ) ) {
$response = $body['api_body'];
}
}
}

$limit_remaining = isset( $response_headers['x_rate_limit_remaining'] ) ? $response_headers['x_rate_limit_remaining'] : false;
$user_24h_limit_remaining = isset( $response_headers['x_user_limit_24hour_remaining'] ) ? $response_headers['x_user_limit_24hour_remaining'] : false;
$app_24h_limit_remaining = isset( $response_headers['x_app_limit_24hour_remaining'] ) ? $response_headers['x_app_limit_24hour_remaining'] : false;

$reset_time_msg = '';
$time_diff = 0;
$max_reset = 0;
$log_limit_msg = __( 'X posting limit reached. Sharing on X will be skipped.', 'tweet-old-post' ) . ' (' . __( 'Learn more about X limits at', 'tweet-old-post' ) . ' https://developer.twitter.com/en/docs/twitter-api/rate-limits). ';

if ( false !== $limit_remaining && $limit_remaining <= 0 ) {
$reset = isset( $response_headers['x_rate_limit_reset'] ) ? $response_headers['x_rate_limit_reset'] : false; // in UTC epoch seconds

if ( $reset ) {
$time_diff = max( $time_diff, $reset - time() );
$max_reset = max( $max_reset, $reset );

$reset_time_msg .= '(' . __( '"x-rate-limit-remaining" will reset at:', 'tweet-old-post' ) . ' ' . date( 'Y-m-d H:i:s', $reset ) . ' UTC' . ')';
}
}

if ( false !== $user_24h_limit_remaining && $user_24h_limit_remaining <= 0 ) {
$reset = isset( $response_headers['x_user_limit_24hour_reset'] ) ? $response_headers['x_user_limit_24hour_reset'] : false;

if ( $reset ) {
$time_diff = max( $time_diff, $reset - time() );
$max_reset = max( $max_reset, $reset );

$reset_time_msg .= '(' . __( '"x-user-limit-24hour-remaining" will reset at:', 'tweet-old-post' ) . ' ' . date( 'Y-m-d H:i:s', $reset ) . ' UTC' . ')';
}
}

if ( false !== $app_24h_limit_remaining && $app_24h_limit_remaining <= 0 ) {
$reset = isset( $response_headers['x_app_limit_24hour_reset'] ) ? $response_headers['x_app_limit_24hour_reset'] : false;

if ( $reset ) {
$time_diff = max( $time_diff, $reset - time() );
$max_reset = max( $max_reset, $reset );

$reset_time_msg .= '(' . __( '"x-app-limit-24hour-remaining" will reset at:', 'tweet-old-post' ) . ' ' . date( 'Y-m-d H:i:s', $reset ) . ' UTC' . ')';
}
}

if ( 0 < $time_diff ) {
set_transient( $transient_key, $log_limit_msg . __( 'All limits will be fully reset by', 'tweet-old-post' ) . ': ' . date( 'Y-m-d H:i:s', $max_reset ) . ' ' . $reset_time_msg, $time_diff );
}

if ( isset( $response['data'] ) && ! empty( $response['data']['id'] ) ) {
$this->logger->alert_success(
sprintf(
'Successfully shared %s to %s on %s ',
Expand All @@ -648,12 +737,31 @@ public function share( $post_details, $args = array() ) {
);

return true;
} else {
$this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', json_encode( $response ) ) );
$this->rop_get_error_docs( $response );
return false;
}

$msg = 'Invalid response from X server.';
$extra = $response;

if ( isset( $response['detail'] ) ) {
$msg = $response['detail'];
}

if ( ! empty( $server_response['message'] ) ) {
$msg = $server_response['message'];

if ( 'limit_reached' === $server_response['code'] ) {
$extra = json_encode( $response_headers );
}

if ( empty( $extra ) ) {
$extra = $server_response['code'];
}
}

$this->logger->alert_error( sprintf( 'Error posting on X: %s | Additional info: %s', $msg, json_encode( $extra ) ) );
$this->rop_get_error_docs( $response );

return false;
}

/**
Expand Down Expand Up @@ -724,6 +832,44 @@ public function add_account_with_app( $account_data ) {
return true;
}

/**
* This method will load and prepare the account data for Twitter user using the info from the Rop server.
*
* @since 8.4.0
*
* @param array $account_data Twitter pages data.
*
* @return bool
*/
public function add_account_from_rop_server( $account_data ) {
if ( ! $this->is_set_not_empty( $account_data, array( 'id' ) ) ) {
return false;
}
$the_id = $account_data['id'];
$account_data = $account_data['pages'];

$this->service = array(
'id' => $the_id,
'service' => $this->service_name,
'credentials' => $account_data['credentials'],
'public_credentials' => array(
'consumer_key' => array(
'name' => 'API Key',
'value' => '',
'private' => false,
),
'consumer_secret' => array(
'name' => 'API secret key',
'value' => '',
'private' => true,
),
),
'available_accounts' => $this->get_users( (object) $account_data ),
);

return true;
}

/**
* Method to populate additional data.
*
Expand All @@ -736,4 +882,40 @@ public function populate_additional_data( $account ) {
return $account;
}

/**
* Send the post to RoP server for sharing
*
* @param string $sharing_type Post sharing type.
* @return array|WP_Error
*/
public static function rop_share_post_via_server( $sharing_type = 'tw', $post_data, $rop_auth_token ) {
$license_key = 'free';
$plan_id = 0;
if ( 'valid' === apply_filters( 'product_rop_license_status', 'invalid' ) ) {
$license_key = apply_filters( 'product_rop_license_key', 'free' );
$plan_id = apply_filters( 'product_rop_license_plan', 0 );
}
// Send API request.
$response = wp_remote_post(
ROP_POST_ON_X_API,
apply_filters(
'rop_post_sharing_api_args',
array(
'timeout' => 100,
'body' => array_merge(
array(
'sharing_type' => $sharing_type,
'license' => $license_key,
'plan_id' => $plan_id,
'site_url' => get_site_url(),
'post_data' => $post_data,
'rop_auth_token' => $rop_auth_token,
)
),
)
)
);

return $response;
}
}
Loading
Loading