HEX
Server: Apache/2.4.37 (CentOS Stream) OpenSSL/1.1.1k
System: Linux ysnet.com.tw 4.18.0-553.5.1.el8.x86_64 #1 SMP Tue May 21 05:46:01 UTC 2024 x86_64
User: test (521)
PHP: 7.4.33
Disabled: NONE
Upload Files
File: /var/www/test/fm_send_messages_to_dc.php
<?php
// 將 DISCORD_BOT_TOKEN 設為環境變數,避免把 Token 寫死在檔案裡
$token = 'MTQyODY1NTAyMzQxOTgxODAxNQ.GVhBDQ.m64Lv0GtJ5I6WfKzSLoeSEJ8yvt44zukEX7iME';
if (!$token) {
    http_response_code(500);
    echo json_encode(['error' => 'Missing DISCORD_BOT_TOKEN env var']);
    exit;
}

// 參數
$user_id = isset($_GET['user_id']) ? trim($_GET['user_id']) : '';
$message = isset($_GET['message']) ? trim($_GET['message']) : '';

if ($message === '') {
    http_response_code(400);
    echo json_encode(['error' => 'message 不能為空']);
    exit;
}
if (mb_strlen($message) > 2000) {
    http_response_code(400);
    echo json_encode(['error' => 'message 超過 2000 字元限制']);
    exit;
}

function discord_post_json($url, $token, $payload) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
        CURLOPT_HTTPHEADER => [
            'Authorization: Bot ' . $token,
            'Content-Type: application/json',
            'User-Agent: YSNETBot (https://ysnet.com.tw, 1.0)'
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER => true, // 連同 header 一起回傳
    ]);
    $raw = curl_exec($ch);
    if ($raw === false) {
        $err = curl_error($ch);
        curl_close($ch);
        return [0, [], ['error' => $err]];
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $headerStr = substr($raw, 0, $headerSize);
    $bodyStr = substr($raw, $headerSize);
    curl_close($ch);

    // 解析 headers
    $headers = [];
    foreach (explode("\r\n", $headerStr) as $line) {
        if (strpos($line, ':') !== false) {
            [$k, $v] = explode(':', $line, 2);
            $headers[trim($k)] = trim($v);
        }
    }

    $body = json_decode($bodyStr, true);
    if ($body === null && $bodyStr !== '') {
        $body = ['raw' => $bodyStr]; // 非 JSON 時保底
    }
    return [$status, $headers, $body];
}

$api = 'https://discord.com/api/v10';

// 1) 建立 DM channel
list($s1, $h1, $b1) = discord_post_json($api . '/users/@me/channels', $token, [
    'recipient_id' => $user_id
]);

if ($s1 !== 200) {
    http_response_code($s1 ?: 500);
    echo json_encode([
        'step' => 'create_dm_channel',
        'status' => $s1,
        'rate_limit_remaining' => $h1['X-RateLimit-Remaining'] ?? null,
        'retry_after' => $h1['Retry-After'] ?? null,
        'error' => $b1['message'] ?? ($b1['error'] ?? 'unknown'),
        'code'  => $b1['code'] ?? null,
        'hint'  => ($s1 === 403)
            ? '對方可能關閉了來自伺服器成員的私訊,或與 Bot 不在同一伺服器。'
            : null
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

$channel_id = $b1['id'] ?? null;
if (!$channel_id) {
    http_response_code(500);
    echo json_encode(['error' => '無法取得 channel_id', 'resp' => $b1], JSON_UNESCAPED_UNICODE);
    exit;
}

// 2) 在該 channel 發送訊息
list($s2, $h2, $b2) = discord_post_json($api . '/channels/' . $channel_id . '/messages', $token, [
    'content' => $message
]);

if (!in_array($s2, [200, 201])) {
    http_response_code($s2 ?: 500);
    echo json_encode([
        'step' => 'send_message',
        'status' => $s2,
        'rate_limit_remaining' => $h2['X-RateLimit-Remaining'] ?? null,
        'retry_after' => $h2['Retry-After'] ?? null,
        'error' => $b2['message'] ?? 'unknown',
        'code'  => $b2['code'] ?? null
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

echo json_encode([
    'ok' => true,
    'channel_id' => $channel_id,
    'message_id' => $b2['id'] ?? null
], JSON_UNESCAPED_UNICODE);
?>