Skip to content

Commit cc0764c

Browse files
committed
Fix: reduce number of createSession calls to comply with Bsky rate limits
1 parent 6b315ed commit cc0764c

File tree

3 files changed

+96
-8
lines changed

3 files changed

+96
-8
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ CREATE TABLE `bbdq` (
3232
`did` varchar(255) NOT NULL,
3333
`identifier` varchar(255) NOT NULL,
3434
`password` varchar(255) NOT NULL,
35+
`accessJwt` TEXT NULL,
36+
`accessJwt_time` DATETIME NULL,
37+
`refreshJwt` TEXT NULL,
38+
`refreshJwt_time` DATETIME NULL,
3539
`iv` varchar(32) NOT NULL,
3640
`script` mediumtext DEFAULT NULL,
3741
`language` varchar(2) NOT NULL DEFAULT 'en',

backend/atproto-functions.php

+88
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,94 @@ function atproto_create_session($provider, $identifier, $password) {
1515
]);
1616
}
1717

18+
function atproto_session($conn, $encryption_key, $id) {
19+
$query = 'SELECT id, provider, did, password, iv, accessJwt, TIMESTAMPDIFF(MINUTE, NOW(), IFNULL(accessJwt_time, \'2000-01-01\')) as accessJwt_time_left, refreshJwt, TIMESTAMPDIFF(MINUTE, NOW(), IFNULL(refreshJwt_time, \'2000-01-01\')) as refreshJwt_time_left FROM bbdq WHERE id = ?';
20+
21+
$stmt = $conn->prepare($query);
22+
$stmt->bind_param('i', $id);
23+
$stmt->execute();
24+
$result = $stmt->get_result();
25+
$stmt->close();
26+
27+
if (!$result->num_rows) {
28+
return ['error' => 'NO_SUCH_USER'];
29+
}
30+
31+
$row = $result->fetch_assoc();
32+
33+
// Is the access token still valid?
34+
if ($row['accessJwt_time_left'] > 5) {
35+
$access_token = openssl_decrypt($row['accessJwt'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
36+
37+
return [
38+
'accessJwt' => $access_token,
39+
'did' => $row['did'],
40+
];
41+
}
42+
43+
// If not, do we have a valid refresh token?
44+
45+
$provider = $row['provider'];
46+
if (!$provider) {
47+
$provider = 'https://bsky.social';
48+
}
49+
50+
if ($row['refreshJwt_time_left'] > 5) {
51+
$refresh_token = openssl_decrypt($row['refreshJwt'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
52+
53+
// Fetch a new access token
54+
$session = fetch( $provider . '/xrpc/com.atproto.server.refreshSession', [
55+
'token' => $refresh_token,
56+
]);
57+
58+
if (isset($session['accessJwt'])) {
59+
// Save new token to database ...
60+
61+
// TODO: Check headers for token lifespan instead of assuming two hours
62+
$encrypted_access_token = openssl_encrypt($session['accessJwt'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
63+
$query = 'UPDATE bbdq SET accessJwt = ?, accessJwt_time = DATE_ADD(NOW(), INTERVAL 2 HOUR) WHERE id = ?';
64+
$stmt = $conn->prepare($query);
65+
$stmt->bind_param('si', $encrypted_access_token, $id);
66+
$stmt->execute();
67+
$stmt->close();
68+
69+
// Return the session
70+
return $session;
71+
}
72+
}
73+
74+
// No valid tokens found, so let's create a new one.
75+
$password = openssl_decrypt($row['password'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
76+
77+
$session = fetch( $provider . '/xrpc/com.atproto.server.createSession', [
78+
'body' => [
79+
'identifier' => $row['did'],
80+
'password' => $password,
81+
],
82+
]);
83+
84+
if (isset($session['accessJwt'])) {
85+
// Save new tokens to database ...
86+
87+
// TODO: Check headers for token lifespan instead of assuming two hours / two months
88+
$encrypted_access_token = openssl_encrypt($session['accessJwt'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
89+
$encrypted_refresh_token = openssl_encrypt($session['refreshJwt'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
90+
$query = 'UPDATE bbdq SET accessJwt = ?, accessJwt_time = DATE_ADD(NOW(), INTERVAL 2 HOUR), refreshJwt = ?, refreshJwt_time = DATE_ADD(NOW(), INTERVAL 58 DAY) WHERE id = ?';
91+
$stmt = $conn->prepare($query);
92+
$stmt->bind_param('ssi', $encrypted_access_token, $encrypted_refresh_token, $id);
93+
$stmt->execute();
94+
$stmt->close();
95+
96+
// Return the session
97+
return $session;
98+
}
99+
100+
// Nothing worked :-(
101+
return [
102+
'error' => 'Could not create session.',
103+
];
104+
}
105+
18106
// Post thread to bluesky
19107
function post_bsky_thread($text, $session, $options = []) {
20108

backend/bot.php

+4-8
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ function run_bot() {
2525
global $encryption_key;
2626

2727
$post_length = 300;
28-
2928
// Get bots
30-
$query = 'SELECT id, provider, identifier, did, password, iv, script, msg, actionIfLong, language FROM bbdq WHERE active = 1 AND TIMESTAMPDIFF(MINUTE, lastPost, NOW()) >= minutesBetweenPosts - 2';
29+
$query = 'SELECT id, provider, script, msg, actionIfLong, language FROM bbdq WHERE active = 1 AND TIMESTAMPDIFF(MINUTE, lastPost, NOW()) >= minutesBetweenPosts - 2';
3130
$stmt = $conn->prepare($query);
3231
$stmt->execute();
3332
$result = $stmt->get_result();
@@ -62,8 +61,7 @@ function run_bot() {
6261
continue;
6362
}
6463
$provider = $row['provider'] ?: 'https://bsky.social';
65-
$password = openssl_decrypt($row['password'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
66-
$session = atproto_create_session($provider, $row['did'], $password);
64+
$session = atproto_session($conn, $encryption_key, $row['id']);
6765
if (isset($session['error'])) {
6866
// Wrong bluesky username/password
6967
continue;
@@ -97,7 +95,7 @@ function check_replies() {
9795
$post_length = 300;
9896

9997
// Get all active bots
100-
$query = 'SELECT id, provider, did, identifier, password, iv, script, reply, actionIfLong, language, lastNotification FROM bbdq WHERE active = 1';
98+
$query = 'SELECT id, provider, script, reply, actionIfLong, language, lastNotification FROM bbdq WHERE active = 1';
10199
$stmt = $conn->prepare($query);
102100
$stmt->execute();
103101
$result = $stmt->get_result();
@@ -121,9 +119,7 @@ function check_replies() {
121119
// Check for new replies
122120
$provider = $row['provider'] ?: 'https://bsky.social';
123121

124-
$password = openssl_decrypt($row['password'], 'aes-256-cbc', $encryption_key, 0, hex2bin($row['iv']));
125-
126-
$session = atproto_create_session($provider, $row['did'], $password);
122+
$session = atproto_session($conn, $encryption_key, $row['id']);
127123
if (isset($session['error'])) {
128124
// Wrong bluesky username/password
129125
continue;

0 commit comments

Comments
 (0)