· 5 years ago · Dec 28, 2020, 01:26 PM
1<?php
2
3
4namespace DisInfo\Util;
5
6
7use DisInfo\Exception\BuzzSumoMonthLimitException;
8use DisInfo\Repository\LinkRepository;
9
10class BuzzSumo
11{
12 public const ENGAGEMENT_META_KEY = 'buzz_sumo_total_engagement';
13 public const ALEXA_RANK_META_KEY = 'alexa_rank';
14
15 public static function getReportTotalEngagementForAdmin(int $postId)
16 {
17 $totalEngagement = self::getReportTotalEngagement($postId);
18
19 return ('' === $totalEngagement) ? 'Not Calculated' : $totalEngagement;
20 }
21
22 public static function getReportTotalEngagement(int $postId)
23 {
24 return get_post_meta($postId, self::ENGAGEMENT_META_KEY, true);
25 }
26
27 public static function getReportAlexaRant(int $postId)
28 {
29 return get_post_meta($postId, self::ALEXA_RANK_META_KEY, true);
30 }
31
32 public static function updateReportTotalEngagement(int $postId): array
33 {
34 $buzzSumoData = self::calculateTotalEngagementForReport($postId);
35 update_post_meta($postId, self::ENGAGEMENT_META_KEY, $buzzSumoData['totalEngagement']);
36 update_post_meta($postId, self::ALEXA_RANK_META_KEY, $buzzSumoData['alexaRank']);
37
38 return $buzzSumoData;
39 }
40
41 private static function calculateTotalEngagementForReport(int $postId): array
42 {
43 global $wpdb;
44
45 $links = LinkRepository::getInstance()->findByPostId($postId);
46 if (empty($links)) {
47 // If have no links via new interface try extract links from text meta field
48 $links = LinksDecoder::extractLinksFromText(get_post_meta($postId, 'disinfo_link', true));
49 } else {
50 $links = wp_list_pluck($links, 'url');
51 }
52
53 $totalEngagement = 0;
54 $alexaRank = 0;
55 foreach ($links as $i => $link) {
56 $buzzSumoData = self::calculateTotalEngagementByUrl($link);
57 if (false === $buzzSumoData) {
58 continue;
59 }
60 $totalEngagement += $buzzSumoData['total_shares'];
61 $alexaRank += $buzzSumoData['alexa_rank'];
62 if (0 === ($i % 5)) { //Ping connection when process many links
63 $wpdb->check_connection();
64 }
65 }
66
67 return [
68 'totalEngagement' => $totalEngagement,
69 'alexaRank' => $alexaRank,
70 ];
71 }
72
73 private static function calculateTotalEngagementByUrl(string $url)
74 {
75 $apiKey = env('BUZZ_SUMO_API_KEY');
76 if (!$apiKey) {
77 throw new \RuntimeException('BuzzSumo API Key if required');
78 }
79
80 if (LinkHelper::isYoutubeUrl($url)) {
81 $url = remove_query_arg('t', $url);
82 }
83
84 $response = wp_remote_get(
85 add_query_arg(
86 [
87 'q' => urlencode($url),
88 'num_days' => 365,
89 'result_type' => 'total',
90 'api_key' => $apiKey,
91 ],
92 'https://api.buzzsumo.com/search/articles.json'
93 ),
94 [
95 'timeout' => 10,
96 ]
97 );
98
99 if ($response instanceof \WP_Error) {
100 error_log($response->get_error_message());
101
102 return false;
103 }
104
105
106 if ((int)$response['response']['code'] !== 200) {
107 error_log($response['body']);
108
109 return false;
110 }
111
112 $headers = $response['headers'];
113
114 //https://developers.buzzsumo.com/reference#rate-limits
115 if (0 === (int)$headers['X-RateLimit-Remaining']) {
116 $secondsLeftToNewLimit = absint($headers['X-RateLimit-Reset'] - time()) + 5;
117 sleep($secondsLeftToNewLimit);
118 }
119
120 if (0 === (int)$headers['X-RateLimit-Month-Remaining']) {
121 throw new BuzzSumoMonthLimitException('Your month BuzzSumo API month limit is over!');
122 }
123
124 $data = \json_decode($response['body'], true);
125
126 if (empty($data['results'][0]['total_shares']) || empty($data['results'][0]['alexa_rank'])) {
127 return false;
128 }
129
130 return [
131 'total_shares' => (int)$data['results'][0]['total_shares'],
132 'alexa_rank' => (int)$data['results'][0]['alexa_rank'],
133 ];
134 }
135
136}
137