Skip to content

Commit 55d15e2

Browse files
committed
add X api
1 parent 85d24b2 commit 55d15e2

15 files changed

Lines changed: 340 additions & 20 deletions

README.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ A set of Laravel API service providers.
1313
- Slack
1414
- Telegram
1515
- WHM
16+
- X (Twitter)
1617
- ZADomains
1718

1819
## Installation
@@ -59,6 +60,10 @@ return [
5960
'server' => env('WHM_SERVER', 'https://server.example.com:2087'),
6061
],
6162
63+
'x' => [
64+
'bearer_token' => env('X_BEARER_TOKEN'),
65+
],
66+
6267
'za_domains' => [
6368
'username' => env('ZA_DOMAINS_USERNAME'),
6469
'password' => env('ZA_DOMAINS_PASSWORD'),
@@ -69,26 +74,31 @@ return [
6974
## Usage
7075

7176
```php
72-
use Eugenefvdm\Api\Facades\BulkSMS;
73-
$bulkSMS = BulkSMS::sendSMS("Hello SMS!", ["27600000000"]);
77+
use Eugenefvdm\Api\Facades\Bulksms;
78+
$bulkSMS = Bulksms::sendSMS("Hello SMS!", ["27600000000"]);
7479

7580
use Eugenefvdm\Api\Facades\Discord;
7681
$discord = Discord::getUser("123456789012345678");
7782

78-
use Eugenefvdm\Api\Facades\HelloPeter;
79-
$helloPeter = HelloPeter::getUnrepliedReviews();
83+
use Eugenefvdm\Api\Facades\Hellopeter;
84+
$hellopeterUnrepliedReviews = Hellopeter::getUnrepliedReviews();
8085

8186
use Eugenefvdm\Api\Facades\Slack;
82-
$slack = Slack::sendText("Hello Slack!");
87+
$textSendResult = Slack::sendText("Hello Slack!");
8388

8489
use Eugenefvdm\Api\Facades\Telegram;
85-
$telegram = Telegram::sendMessage("Hi Telegram!");
90+
$messageSendResult = Telegram::sendMessage("Hi Telegram!");
91+
92+
use Eugenefvdm\Api\Facades\Whm;
93+
$whmBandwidth = Whm::bandwidth();
8694

87-
use Eugenefvdm\Api\Facades\WHM;
88-
$bandwidth = WHM::bandwidth();
95+
use Eugenefvdm\Api\Facades\X;
96+
$userId = X::userId("eugenefvdm");
97+
$tweets = X::tweets($userId['data']['id'], 5);
98+
$userWithLimits = X::userWithRateLimits("eugenefvdm");
8999

90-
use Eugenefvdm\Api\Facades\ZADomains;
91-
$zadomainsRegistrant = ZADomains::registrant("example.co.za");
100+
use Eugenefvdm\Api\Facades\Zadomains;
101+
$zadomainsRegistrant = Zadomains::registrant("example.co.za");
92102
```
93103

94104
## Testing

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"homepage": "https://github.com/eugenefvdm/api",
2626
"require": {
2727
"php": "^8.1",
28-
"illuminate/support": "^8.12|^9.0|^10.0|^11.0|^12.0"
28+
"illuminate/support": "^8.12|^9.0|^10.0|^11.0|^12.0",
29+
"spatie/laravel-ray": "^1.40"
2930
},
3031
"require-dev": {
3132
"guzzlehttp/guzzle": "^7.9",

config/api.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?php
22

33
return [
4-
'bulk_sms' => [
5-
'username' => env('BULK_SMS_USERNAME'),
6-
'password' => env('BULK_SMS_PASSWORD'),
4+
'bulksms' => [
5+
'username' => env('BULKSMS_USERNAME'),
6+
'password' => env('BULKSMS_PASSWORD'),
77
],
88

99
'discord' => [
1010
'bot_token' => env('DISCORD_BOT_TOKEN'),
1111
],
1212

13-
'hello_peter' => [
14-
'api_key' => env('HELLO_PETER_API_KEY'),
13+
'hellopeter' => [
14+
'api_key' => env('HELLOPETER_API_KEY'),
1515
],
1616

1717
'slack' => [
@@ -29,8 +29,12 @@
2929
'server' => env('WHM_SERVER'),
3030
],
3131

32-
'za_domains' => [
33-
'username' => env('ZA_DOMAINS_USERNAME'),
34-
'password' => env('ZA_DOMAINS_PASSWORD'),
32+
'x' => [
33+
'bearer_token' => env('X_BEARER_TOKEN'),
3534
],
35+
36+
'zadomains' => [
37+
'username' => env('ZADOMAINS_USERNAME'),
38+
'password' => env('ZADOMAINS_PASSWORD'),
39+
],
3640
];

src/ApiManager.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public function whm()
4848
return $this->app->make('whm');
4949
}
5050

51+
public function x()
52+
{
53+
return $this->app->make('x');
54+
}
55+
5156
/**
5257
* Magic method to dynamically access services
5358
*/

src/ApiServiceProvider.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function register()
3939
// Register HelloPeter
4040
$this->app->singleton(Hellopeter::class, function ($app) {
4141
return new Hellopeter(
42-
Config::get('api.hello_peter.api_key')
42+
Config::get('api.hellopeter.api_key')
4343
);
4444
});
4545
$this->app->alias(Hellopeter::class, 'hellopeter');
@@ -79,6 +79,14 @@ public function register()
7979
);
8080
});
8181
$this->app->alias(Whm::class, 'whm');
82+
83+
// Register X
84+
$this->app->singleton(X::class, function ($app) {
85+
return new X(
86+
Config::get('api.x.bearer_token')
87+
);
88+
});
89+
$this->app->alias(X::class, 'x');
8290
}
8391

8492
public function boot()

src/Facades/X.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Eugenefvdm\Api\Facades;
4+
5+
use Illuminate\Support\Facades\Facade;
6+
7+
/**
8+
* @method static array userId(string $username)
9+
* @method static array tweets(string $userId, int $maxResults = 5)
10+
* @method static array userWithRateLimits(string $username)
11+
*
12+
* @see \Eugenefvdm\Api\X
13+
*/
14+
class X extends Facade
15+
{
16+
/**
17+
* Get the registered name of the component.
18+
*
19+
* @return string
20+
*/
21+
protected static function getFacadeAccessor()
22+
{
23+
return 'x';
24+
}
25+
}

src/X.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace Eugenefvdm\Api;
4+
5+
use Illuminate\Http\Client\PendingRequest;
6+
use Illuminate\Support\Facades\Http;
7+
8+
class X
9+
{
10+
private string $bearerToken;
11+
private string $baseUrl = 'https://api.twitter.com/2';
12+
private ?PendingRequest $client = null;
13+
14+
public function __construct(string $bearerToken)
15+
{
16+
$this->bearerToken = $bearerToken;
17+
}
18+
19+
/**
20+
* Get the HTTP client instance
21+
*/
22+
private function client(): PendingRequest
23+
{
24+
if (! $this->client) {
25+
$this->client = Http::baseUrl($this->baseUrl)
26+
->withHeaders([
27+
'Authorization' => "Bearer {$this->bearerToken}",
28+
'Content-Type' => 'application/json',
29+
]);
30+
}
31+
32+
return $this->client;
33+
}
34+
35+
/**
36+
* Set a custom HTTP client (used for testing)
37+
*/
38+
public function setClient(PendingRequest $client): void
39+
{
40+
$this->client = $client;
41+
}
42+
43+
/**
44+
* Get user ID by username
45+
*
46+
* @param string $username The username to lookup
47+
* @return array User data including id, name, and username
48+
*/
49+
public function userId(string $username): array
50+
{
51+
$response = $this->client()->get("/users/by/username/{$username}");
52+
return $response->json();
53+
}
54+
55+
/**
56+
* Get tweets for a user
57+
*
58+
* @param string $userId The user ID to get tweets for
59+
* @param int $maxResults Maximum number of tweets to retrieve (default: 5)
60+
* @return array Tweets data including tweets and metadata
61+
*/
62+
public function tweets(string $userId, int $maxResults = 5): array
63+
{
64+
$response = $this->client()->get("/users/{$userId}/tweets", [
65+
'max_results' => $maxResults,
66+
]);
67+
return $response->json();
68+
}
69+
70+
/**
71+
* Get user data and rate limits
72+
*
73+
* @param string $username The username to lookup
74+
* @return array User data and rate limit information
75+
*/
76+
public function userWithRateLimits(string $username): array
77+
{
78+
$response = $this->client()->withHeaders([
79+
'Accept' => 'application/json',
80+
])->get("/users/by/username/{$username}");
81+
82+
return [
83+
'data' => $response->json(),
84+
'rate_limits' => [
85+
'limit' => $response->header('x-rate-limit-limit'),
86+
'remaining' => $response->header('x-rate-limit-remaining'),
87+
'reset' => $response->header('x-rate-limit-reset'),
88+
],
89+
];
90+
}
91+
}

src/ZADomains.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public function registrant(string $domainName): string
5757
$result = $this->getDomainSelect($domainName);
5858
$data = json_decode($result->Domain_SelectResult, true);
5959

60+
ray($data);
61+
6062
if (! isset($data['Response_Value'])) {
6163
throw new \RuntimeException('Unable to fetch registrant information');
6264
}

tests/Feature/X/RateLimitTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
use Eugenefvdm\Api\X;
4+
use Illuminate\Support\Facades\Http;
5+
6+
test('tweets returns rate limit error when limit is hit', function () {
7+
$stub = json_decode(file_get_contents(__DIR__.'/../../stubs/x/rate_limit.json'), true);
8+
9+
Http::fake([
10+
'api.twitter.com/2/users/74251818/tweets*' => Http::response($stub, 429),
11+
]);
12+
13+
$x = new X('test_bearer_token');
14+
15+
$result = $x->tweets('74251818');
16+
17+
expect($result)
18+
->toBe($stub)
19+
->and($result['title'])->toBe('Too Many Requests')
20+
->and($result['detail'])->toBe('Too Many Requests')
21+
->and($result['type'])->toBe('about:blank')
22+
->and($result['status'])->toBe(429);
23+
});

tests/Feature/X/RateLimitsTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use Eugenefvdm\Api\X;
4+
use Illuminate\Support\Facades\Http;
5+
6+
test('userWithRateLimits returns user data and rate limits', function () {
7+
$stub = json_decode(file_get_contents(__DIR__.'/../../stubs/x/user_id.json'), true);
8+
9+
Http::fake([
10+
'api.twitter.com/2/users/by/username/eugenefvdm' => Http::response($stub, 200, [
11+
'x-rate-limit-limit' => '1200000',
12+
'x-rate-limit-remaining' => '1199998',
13+
'x-rate-limit-reset' => '1738820163',
14+
]),
15+
]);
16+
17+
$x = new X('test_bearer_token');
18+
19+
$result = $x->userWithRateLimits('eugenefvdm');
20+
21+
expect($result)
22+
->toHaveKeys(['data', 'rate_limits'])
23+
->and($result['data'])->toBe($stub)
24+
->and($result['rate_limits'])->toHaveKeys(['limit', 'remaining', 'reset'])
25+
->and($result['rate_limits']['limit'])->toBe('1200000')
26+
->and($result['rate_limits']['remaining'])->toBe('1199998')
27+
->and($result['rate_limits']['reset'])->toBe('1738820163');
28+
});

0 commit comments

Comments
 (0)