Skip to content

Instantly share code, notes, and snippets.

@gbhorwood
Created January 23, 2025 18:21
Show Gist options
  • Save gbhorwood/729e7e4975e40474f9342f1e70467106 to your computer and use it in GitHub Desktop.
Save gbhorwood/729e7e4975e40474f9342f1e70467106 to your computer and use it in GitHub Desktop.
Example Macrame script
#!/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