Created
January 23, 2025 18:21
-
-
Save gbhorwood/729e7e4975e40474f9342f1e70467106 to your computer and use it in GitHub Desktop.
Example Macrame script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
/** | |
* Example Macrame script that fetches mastodon followers. | |
* | |
* https://macrame.fruitbat.studio/Installation.html | |
* https://github.com/gbhorwood/Macrame | |
*/ | |
require __DIR__ . '/vendor/autoload.php'; | |
use Gbhorwood\Macrame\Macrame; | |
/** | |
* Instantiate a Macrame object. | |
* The argument is the name of the script as seen by ps(1) | |
*/ | |
$macrame = new Macrame("Example Macrame Script"); | |
/** | |
* Enforce that the script only runs if executed on the command line | |
*/ | |
if ($macrame->running()) { | |
// ENTRY POINT | |
/** | |
* Validate that the host system can run Macrame scripts. Exit on failure | |
*/ | |
$macrame->preflight(); | |
/** | |
* Handle the -v or --version arguments | |
*/ | |
if ($macrame->args('v')->exists() || $macrame->args('version')->exists()) { | |
$macrame->text('1.0')->write(); | |
$macrame->exit(); | |
} | |
/** | |
* Handle the --instance= argument if present, or poll user for instance | |
* with dynamic menu if not. | |
*/ | |
$instance = $macrame->args('instance')->first(); | |
if (!$instance) { | |
$instance = menuInstance($macrame); | |
} | |
/** | |
* Handle the --username= argument if present, or poll user for username | |
* with text input if not | |
*/ | |
$username = $macrame->args('username')->first(); | |
if (!$username) { | |
$username = inputUsername($instance, $macrame); | |
} | |
/** | |
* Read the value of the --outfile= argument if any | |
*/ | |
$outfile = $macrame->args('outfile')->first(); | |
/** | |
* A function to fetch an ascii table of followers from mastodon for the user | |
* defined by $username and $instance. | |
* @param string $username | |
* @param string $instance | |
* @param Macrame $macrame | |
* @return string | |
*/ | |
$getFollowersTable = function (string $username, string $instance, Macrame $macrame) { | |
// call mastodon to get the user id for the username | |
$userId = mastodonUserId($username, $instance, $macrame); | |
// call mastodon to get the followers for the user id | |
$followers = mastodonFollowers($userId, $instance, $macrame); | |
// format the array of followers into a table | |
$followersTable = tableFollowers($followers, $macrame); | |
return $followersTable; | |
}; | |
/** | |
* Run the $getFollowersTable() function in the background and display an animated | |
* spinner to the user while it is running. | |
*/ | |
$followersTable = $macrame->spinner('cycle 2') | |
->prompt('fetching ') | |
->speed('fast') | |
->run($getFollowersTable, [$username, $instance, $macrame]); | |
/** | |
* If the --outfile= argument was passed, write the followers table to the file | |
* otherwise write the followers table to STDOUT, paged to screen height | |
*/ | |
if ($outfile) { | |
$macrame->file($outfile)->write($followersTable); | |
} else { | |
$macrame->text($followersTable)->page(); | |
} | |
// exit cleanly | |
$macrame->exit(); | |
} | |
/** | |
* Display a dynamic menu of instances to the user, return | |
* the selected text. | |
* | |
* @param Macrame $macrame | |
* @return string | |
*/ | |
function menuInstance(Macrame $macrame): string | |
{ | |
$header = "Select your instance"; | |
// list of options in the menu | |
$instances = [ | |
'phpc.social', | |
'mastodon.social', | |
'mstdn.ca', | |
]; | |
// display the menu, return the selected text | |
return $macrame->menu() | |
->erase() // erase the menu after selection | |
->interactive($instances, $header); | |
} | |
/** | |
* Display a text input to the user, return the input text. | |
* | |
* @param string $instance | |
* @param Macrame $macrame | |
* @return string | |
*/ | |
function inputUsername(string $instance, Macrame $macrame): string | |
{ | |
// the prompt for the text input, with bold styling using tags | |
$prompt = $macrame->text("<!BOLD!>Username<!CLOSE!> (for $instance): ") | |
->get(); | |
/* alternate method to apply bold styling: | |
$prompt = $macrame->text('Username ') | |
->style('bold') | |
->get()."(for $instance): "; | |
*/ | |
// read one line of user input, return the text | |
return $macrame->input() | |
->readline($prompt); | |
} | |
/** | |
* Convert the followers data returned by the mastodon instance into an ascii | |
* table and return. | |
* | |
* @param array $followers The array returned by mastodonFollowers() | |
* @param Macrame $macrame | |
* @return string | |
*/ | |
function tableFollowers(array $followers, Macrame $macrame): string | |
{ | |
// extract the 'acct' and 'display_name' fields from each element for use as table rows | |
$data = array_map(fn ($f) => [$f->acct, $f->display_name], $followers); | |
$headers = ['Account', 'Display Name']; | |
// build the ascii table and return | |
return $macrame->table($headers, $data)->get(); | |
} | |
/** | |
* Fetch the id of the user $username from the mastodon instance $instance | |
* | |
* @param string $username | |
* @param string $instance | |
* @param Macrame $macrame | |
* @return int | |
*/ | |
function mastodonUserId(string $username, string $instance, Macrame $macrame): int | |
{ | |
$url = "https://$instance/api/v1/accounts/lookup?acct=$username"; | |
// make the api call and handle errors | |
try { | |
return get($url, $macrame)->id; | |
} catch (\Exception $e) { | |
// display error text and exit the script | |
$macrame->text("User '$username' not found on '$instance'")->error(); | |
$macrame->exit(); | |
} | |
} | |
/** | |
* Fetch the array of the followers for the user $userId from the mastodon instance $instance | |
* | |
* @param string $userId | |
* @param string $instance | |
* @param Macrame $macrame | |
* @return int | |
*/ | |
function mastodonFollowers(int $userId, string $instance, Macrame $macrame): array | |
{ | |
$url = "https://$instance/api/v1/accounts/$userId/followers?limit=80"; | |
try { | |
return get($url, $macrame); | |
} catch (\Exception $e) { | |
$macrame->text("Followers for '$username' not found on '$instance'")->error(); | |
$macrame->exit(); | |
} | |
} | |
/** | |
* Execute curl GET call to $url and return result | |
* | |
* @param string $url | |
* @param Macrame $macrame | |
* @return mixed | |
*/ | |
function get(string $url, Macrame $macrame) | |
{ | |
$headers = [ | |
'Accept: application/json', | |
]; | |
$ch = curl_init($url); | |
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); | |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
$result = curl_exec($ch); | |
$header = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); | |
if ($header !== 201 && $header !== 200) { | |
curl_close($ch); | |
$macrame->text("Call to $url returned $header")->warning(); | |
throw new \Exception(); | |
} | |
curl_close($ch); | |
return json_decode($result); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment