PHP Classes

File: scripts/dlimport_bbslist.php

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   scripts/dlimport_bbslist.php   Download  
File: scripts/dlimport_bbslist.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 6,426 bytes
 

Contents

Class file image Download
#!/usr/bin/env php
<?php

/**
 * Download and import the current monthly Telnet BBS Guide list.
 *
 * Usage:
 * php scripts/dlimport_bbslist.php [options]
 *
 * Options:
 * --month=MM Month to download (01-12). Defaults to current month.
 * --year=YYYY Year to download. Defaults to current year.
 * --file=NAME Specific archive filename, e.g. ibbs0526.zip.
 * --dry-run Pass --dry-run to import_bbslist.php.
 * --quiet Suppress normal output except errors.
 * --help, -h Show this help.
 */

require_once __DIR__ . '/../vendor/autoload.php';
require_once
__DIR__ . '/../src/functions.php';

const
BBSLIST_BASE_URL = 'https://www.telnetbbsguide.com/bbslist/';

/**
 * Print script usage.
 */
function printUsage(): void
{
    echo <<<HELP
Usage: php scripts/dlimport_bbslist.php [options]

Downloads the monthly Telnet BBS Guide ZIP from:
  https://www.telnetbbsguide.com/bbslist/

By default, the archive name is generated from the current month and year:
  ibbsMMYY.zip

Options:
  --month=MM Month to download (01-12). Defaults to current month.
  --year=YYYY Year to download. Defaults to current year.
  --file=NAME Specific archive filename, e.g. ibbs0526.zip.
  --dry-run Download and parse without writing to the database.
  --quiet Suppress normal output except errors.
  --help, -h Show this help.

Examples:
  php scripts/dlimport_bbslist.php
  php scripts/dlimport_bbslist.php --file=ibbs0526.zip --dry-run
  php scripts/dlimport_bbslist.php --month=05 --year=2026 --quiet

Exit codes:
  0 Success
  1 Error

HELP;
}

/**
 * Write a message to stdout unless quiet mode is enabled.
 */
function output(string $message, bool $quiet = false): void
{
    if (!
$quiet) {
        echo
$message . PHP_EOL;
    }
}

/**
 * Download a URL to a local file.
 *
 * @throws RuntimeException when the download fails or returns suspicious data.
 */
function downloadFile(string $url, string $destination, bool $quiet = false): void
{
   
output("Downloading: {$url}", $quiet);

   
$context = stream_context_create([
       
'http' => [
           
'timeout' => 300,
           
'user_agent' => 'BinktermPHP BBS List Importer',
           
'follow_location' => true,
           
'max_redirects' => 5,
           
'ignore_errors' => true,
        ],
       
'ssl' => [
           
'verify_peer' => true,
           
'verify_peer_name' => true,
        ],
    ]);

   
$content = @file_get_contents($url, false, $context);
    if (
$content === false) {
       
$error = error_get_last();
        throw new
RuntimeException('Download failed: ' . ($error['message'] ?? 'unknown error'));
    }

   
$statusLine = $http_response_header[0] ?? '';
    if (
$statusLine !== '' && preg_match('/^HTTP\/\S+\s+(\d+)/', $statusLine, $matches)) {
       
$status = (int)$matches[1];
        if (
$status < 200 || $status >= 300) {
            throw new
RuntimeException("Download failed with HTTP status {$status}");
        }
    }

    if (
strlen($content) < 1000) {
        throw new
RuntimeException('Downloaded file is too small: ' . strlen($content) . ' bytes');
    }

   
$bytes = file_put_contents($destination, $content, LOCK_EX);
    if (
$bytes === false) {
        throw new
RuntimeException("Could not write downloaded file: {$destination}");
    }

   
output('Downloaded ' . number_format($bytes) . ' bytes to ' . $destination, $quiet);
}

/**
 * Run import_bbslist.php with the downloaded ZIP file.
 *
 * @throws RuntimeException when the import script fails.
 */
function runImport(string $zipPath, bool $dryRun = false, bool $quiet = false): void
{
   
$importScript = __DIR__ . '/import_bbslist.php';
    if (!
file_exists($importScript)) {
        throw new
RuntimeException("Import script not found: {$importScript}");
    }

   
$cmd = sprintf(
       
'php %s %s%s%s 2>&1',
       
escapeshellarg($importScript),
       
escapeshellarg($zipPath),
       
$dryRun ? ' --dry-run' : '',
       
$quiet ? ' --quiet' : ''
   
);

   
output('Running import_bbslist.php...', $quiet);

   
$lines = [];
   
$returnCode = 0;
   
exec($cmd, $lines, $returnCode);

    if (!
$quiet) {
        foreach (
$lines as $line) {
            echo
$line . PHP_EOL;
        }
    }

    if (
$returnCode !== 0) {
        throw new
RuntimeException("Import failed with exit code {$returnCode}: " . implode(PHP_EOL, $lines));
    }
}

$args = array_slice($argv, 1);
if (
in_array('--help', $args, true) || in_array('-h', $args, true)) {
   
printUsage();
    exit(
0);
}

$month = date('m');
$year = date('Y');
$fileName = null;
$dryRun = false;
$quiet = false;

foreach (
$args as $arg) {
    if (
$arg === '--dry-run') {
       
$dryRun = true;
        continue;
    }

    if (
$arg === '--quiet' || $arg === '-q') {
       
$quiet = true;
        continue;
    }

    if (
str_starts_with($arg, '--month=')) {
       
$month = substr($arg, strlen('--month='));
        continue;
    }

    if (
str_starts_with($arg, '--year=')) {
       
$year = substr($arg, strlen('--year='));
        continue;
    }

    if (
str_starts_with($arg, '--file=')) {
       
$fileName = substr($arg, strlen('--file='));
        continue;
    }

   
fwrite(STDERR, "Unknown option: {$arg}\n");
   
fwrite(STDERR, "Use --help for usage.\n");
    exit(
1);
}

try {
    if (
$fileName === null) {
        if (!
preg_match('/^(0[1-9]|1[0-2])$/', $month)) {
            throw new
InvalidArgumentException("Invalid month: {$month}");
        }

        if (!
preg_match('/^\d{4}$/', $year)) {
            throw new
InvalidArgumentException("Invalid year: {$year}");
        }

       
$fileName = sprintf('ibbs%s%s.zip', $month, substr($year, -2));
    }

    if (!
preg_match('/^ibbs\d{4}\.zip$/i', $fileName)) {
        throw new
InvalidArgumentException("Invalid BBS list filename: {$fileName}");
    }

   
$downloadDir = __DIR__ . '/../data/bbslist';
    if (!
is_dir($downloadDir) && !mkdir($downloadDir, 0755, true)) {
        throw new
RuntimeException("Could not create download directory: {$downloadDir}");
    }

   
$zipPath = $downloadDir . DIRECTORY_SEPARATOR . strtolower($fileName);
   
$url = BBSLIST_BASE_URL . strtolower($fileName);

   
downloadFile($url, $zipPath, $quiet);
   
runImport($zipPath, $dryRun, $quiet);

   
output('BBS list download and import complete.', $quiet);
    exit(
0);
} catch (
Throwable $e) {
   
fwrite(STDERR, 'Error: ' . $e->getMessage() . PHP_EOL);
    exit(
1);
}