|
6 | 6 | error_reporting(E_ALL); |
7 | 7 | ini_set('display_errors', getenv('APP_DEBUG') ? '1' : '0'); |
8 | 8 |
|
9 | | -// Twitter API credentials |
10 | | -if (!file_exists(__DIR__ . '/config.php')) { |
11 | | - fwrite(STDERR, "Error: config.php not found. Copy .env.example values into config.php.\n"); |
| 9 | +// Optional file-based credentials. Environment variables override these values. |
| 10 | +if (file_exists(__DIR__ . '/config.php')) { |
| 11 | + require_once __DIR__ . '/config.php'; |
| 12 | +} |
| 13 | + |
| 14 | +$backend = strtolower(trim(getConfigValue('TWITTER_BACKEND', 'twitter'))); |
| 15 | +if ($backend !== 'twitter' && $backend !== 'xquik') { |
| 16 | + fwrite(STDERR, "Error: TWITTER_BACKEND must be twitter or xquik.\n"); |
12 | 17 | exit(1); |
13 | 18 | } |
14 | | -require_once __DIR__ . '/config.php'; |
15 | | - |
16 | | -// Validate that credentials have been configured |
17 | | -$credentials = [ |
18 | | - 'API_KEY' => API_KEY, |
19 | | - 'API_SECRET_KEY' => API_SECRET_KEY, |
20 | | - 'ACCESS_TOKEN' => ACCESS_TOKEN, |
21 | | - 'ACCESS_TOKEN_SECRET' => ACCESS_TOKEN_SECRET, |
22 | | -]; |
23 | | -foreach ($credentials as $name => $value) { |
24 | | - if (empty($value) || strpos($value, 'YOUR_') === 0) { |
25 | | - echo "Error: $name is not configured. Please update config.php with your actual credentials.\n"; |
26 | | - exit(1); |
27 | | - } |
| 19 | + |
| 20 | +if ($backend === 'twitter') { |
| 21 | + validateCredentials([ |
| 22 | + 'API_KEY' => getConfigValue('API_KEY'), |
| 23 | + 'API_SECRET_KEY' => getConfigValue('API_SECRET_KEY'), |
| 24 | + 'ACCESS_TOKEN' => getConfigValue('ACCESS_TOKEN'), |
| 25 | + 'ACCESS_TOKEN_SECRET' => getConfigValue('ACCESS_TOKEN_SECRET'), |
| 26 | + ]); |
28 | 27 | } |
29 | 28 |
|
30 | 29 | // The tweet content variables |
|
54 | 53 | */ |
55 | 54 | } |
56 | 55 |
|
| 56 | +if ($backend === 'xquik') { |
| 57 | + postTweetWithXquik($fullTweet); |
| 58 | + exit(0); |
| 59 | +} |
| 60 | + |
57 | 61 | // Twitter API endpoint for creating a tweet (API v2) |
58 | 62 | $url = 'https://api.twitter.com/2/tweets'; |
59 | 63 |
|
60 | 64 | // OAuth parameters |
61 | 65 | $oauth = [ |
62 | | - 'oauth_consumer_key' => API_KEY, |
| 66 | + 'oauth_consumer_key' => getConfigValue('API_KEY'), |
63 | 67 | 'oauth_nonce' => bin2hex(random_bytes(16)), |
64 | 68 | 'oauth_signature_method' => 'HMAC-SHA1', |
65 | 69 | 'oauth_timestamp' => time(), |
66 | | - 'oauth_token' => ACCESS_TOKEN, |
| 70 | + 'oauth_token' => getConfigValue('ACCESS_TOKEN'), |
67 | 71 | 'oauth_version' => '1.0', |
68 | 72 | ]; |
69 | 73 |
|
|
79 | 83 | $base_info = buildBaseString($url, 'POST', $signature_params); |
80 | 84 |
|
81 | 85 | // Generate the composite signing key |
82 | | -$composite_key = rawurlencode(API_SECRET_KEY) . '&' . rawurlencode(ACCESS_TOKEN_SECRET); |
| 86 | +$composite_key = |
| 87 | + rawurlencode(getConfigValue('API_SECRET_KEY')) . |
| 88 | + '&' . |
| 89 | + rawurlencode(getConfigValue('ACCESS_TOKEN_SECRET')); |
83 | 90 |
|
84 | 91 | // Generate the OAuth signature and add it to the OAuth parameters |
85 | 92 | $oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true)); |
|
107 | 114 | $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
108 | 115 | $curl_err = $response === false ? curl_error($ch) : null; |
109 | 116 |
|
110 | | -// Close cURL |
111 | | -curl_close($ch); |
| 117 | +// PHP 8 releases cURL handles automatically. PHP 8.5 deprecates curl_close(). |
| 118 | +if (PHP_VERSION_ID < 80000) { |
| 119 | + curl_close($ch); |
| 120 | +} |
112 | 121 |
|
113 | 122 | if ($response === false) { |
114 | 123 | $error_message = "Failed to post tweet. cURL error: $curl_err\n"; |
|
148 | 157 |
|
149 | 158 | echo $error_message; |
150 | 159 | logTwitterResponse($http_status, $error_message); |
| 160 | + exit(1); |
| 161 | +} |
| 162 | + |
| 163 | +/** |
| 164 | + * Reads a config value from the environment or config.php. |
| 165 | + * |
| 166 | + * @param string $name |
| 167 | + * @param string $default |
| 168 | + * @return string |
| 169 | + */ |
| 170 | +function getConfigValue($name, $default = '') |
| 171 | +{ |
| 172 | + $environmentValue = getenv($name); |
| 173 | + if ($environmentValue !== false && $environmentValue !== '') { |
| 174 | + return $environmentValue; |
| 175 | + } |
| 176 | + |
| 177 | + return defined($name) ? (string) constant($name) : $default; |
| 178 | +} |
| 179 | + |
| 180 | +/** |
| 181 | + * Validates required credentials. |
| 182 | + * |
| 183 | + * @param array $credentials |
| 184 | + * @return void |
| 185 | + */ |
| 186 | +function validateCredentials($credentials) |
| 187 | +{ |
| 188 | + foreach ($credentials as $name => $value) { |
| 189 | + if (empty($value) || stripos($value, 'your_') === 0) { |
| 190 | + fwrite(STDERR, "Error: $name is not configured.\n"); |
| 191 | + exit(1); |
| 192 | + } |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +/** |
| 197 | + * Posts a tweet through Xquik's REST API. |
| 198 | + * |
| 199 | + * @param string $fullTweet |
| 200 | + * @return void |
| 201 | + */ |
| 202 | +function postTweetWithXquik($fullTweet) |
| 203 | +{ |
| 204 | + $apiKey = getConfigValue('XQUIK_API_KEY'); |
| 205 | + $account = getConfigValue('XQUIK_ACCOUNT'); |
| 206 | + validateCredentials([ |
| 207 | + 'XQUIK_API_KEY' => $apiKey, |
| 208 | + 'XQUIK_ACCOUNT' => $account, |
| 209 | + ]); |
| 210 | + |
| 211 | + $apiBase = rtrim(getConfigValue('XQUIK_API_BASE', 'https://xquik.com/api/v1'), '/'); |
| 212 | + $postfields = json_encode([ |
| 213 | + 'account' => $account, |
| 214 | + 'text' => $fullTweet, |
| 215 | + ], JSON_UNESCAPED_UNICODE); |
| 216 | + if ($postfields === false) { |
| 217 | + fwrite(STDERR, "Error: Could not encode the Xquik request.\n"); |
| 218 | + exit(1); |
| 219 | + } |
| 220 | + |
| 221 | + $ch = curl_init(); |
| 222 | + if ($ch === false) { |
| 223 | + fwrite(STDERR, "Error: Could not initialize cURL.\n"); |
| 224 | + exit(1); |
| 225 | + } |
| 226 | + |
| 227 | + curl_setopt_array($ch, [ |
| 228 | + CURLOPT_URL => $apiBase . '/x/tweets', |
| 229 | + CURLOPT_POST => true, |
| 230 | + CURLOPT_HTTPHEADER => [ |
| 231 | + 'Content-Type: application/json', |
| 232 | + 'x-api-key: ' . $apiKey, |
| 233 | + ], |
| 234 | + CURLOPT_POSTFIELDS => $postfields, |
| 235 | + CURLOPT_RETURNTRANSFER => true, |
| 236 | + ]); |
| 237 | + |
| 238 | + $response = curl_exec($ch); |
| 239 | + $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
| 240 | + $curl_err = $response === false ? curl_error($ch) : null; |
| 241 | + if (PHP_VERSION_ID < 80000) { |
| 242 | + curl_close($ch); |
| 243 | + } |
| 244 | + |
| 245 | + if ($response === false) { |
| 246 | + $error_message = "Failed to post tweet with Xquik. cURL error: $curl_err\n"; |
| 247 | + fwrite(STDERR, $error_message); |
| 248 | + logTwitterResponse($http_status, $error_message); |
| 249 | + exit(1); |
| 250 | + } |
| 251 | + |
| 252 | + $response_data = json_decode($response, true); |
| 253 | + if ($http_status >= 200 && $http_status < 300 && isset($response_data['tweetId'])) { |
| 254 | + $success_message = "Tweet posted successfully with Xquik! Tweet ID: " . $response_data['tweetId']; |
| 255 | + echo $success_message; |
| 256 | + logTwitterResponse($http_status, $success_message); |
| 257 | + return; |
| 258 | + } |
| 259 | + |
| 260 | + $error_message = "Failed to post tweet with Xquik. HTTP Status Code: $http_status\n"; |
| 261 | + if (is_array($response_data) && isset($response_data['message'])) { |
| 262 | + $error_message .= "Message: " . $response_data['message'] . "\n"; |
| 263 | + } |
| 264 | + fwrite(STDERR, $error_message); |
| 265 | + logTwitterResponse($http_status, $error_message); |
| 266 | + exit(1); |
151 | 267 | } |
152 | 268 |
|
153 | 269 | /** |
|
0 commit comments