PotatoHotDog cung cấp một API Tạo Ảnh AI miễn phí mà bạn có thể gọi trực tiếp từ Streamer.bot, mà không cần bất kỳ phần mềm trung gian bên thứ ba nào. Hướng dẫn này cho bạn biết cách thêm một đoạn script C# tùy chỉnh nhỏ vào một hành động con Execute Code của Streamer.bot để tạo ra một hình ảnh AI từ một prompt văn bản và tải nó xuống đĩa.
Đoạn script chỉ cần khóa API PotatoHotDog và một prompt mô tả hình ảnh bạn muốn được tạo, và nó sẽ lưu hình ảnh đã tải xuống vào một thư mục riêng bên trong thư mục cài đặt của Streamer.bot.
Chuẩn bị một khóa API
Nếu bạn chưa có, hãy tạo một khóa API từ bảng điều khiển của bạn và giữ nó sẵn sàng, bạn sẽ cần nó ở bước tiếp theo.
Đi tới các khóa API
Các tham số tiên quyết
Để tạo ra một hình ảnh do AI tạo ra, hai tham số cần được chỉ định trong Streamer.bot trước khi hành động phụ Execute Code chạy. Hay nói cách khác, hai điều này cần được chỉ định:
- Một khóa API.
- Các chỉ dẫn của prompt mô tả hình ảnh bạn muốn tạo.
Chúng tương ứng với các tham số sau của Streamer.bot:
- potatoKey
- potatoInstructions
Bạn chỉ định tham số cho hành động của mình bằng cách nhấp chuột phải vào Sub Actions, sau đó chọn Add, Core, Arguments, và cuối cùng Set Argument.
Thêm hành động Execute Code trong Streamer.bot
Mở Streamer.bot và vào tab Actions. Tạo một hành động mới, hoặc mở một hành động có sẵn, và đặt cho nó một tên bạn sẽ nhận diện được sau này, chẳng hạn như "PotatoHotDog AI Image".
Với hành động được chọn, hãy nhấp Add dưới Sub Actions, sau đó chọn Core và tiếp theo là Execute C# Code. Điều này sẽ thêm một hành động phụ cho phép bạn dán mã C# thô mà Streamer.bot sẽ biên dịch và chạy ngay lập tức.
Dán đoạn script dưới đây vào trình soạn thảo mã. Nó đọc các đối số potatoKey và potatoInstructions, gọi Generate AI Image API của chúng tôi để tạo hình ảnh và lấy URL của nó, và sau đó tải hình ảnh kết quả vào thư mục có tên temp_potatohotdog_images bên trong thư mục cài đặt Streamer.bot của bạn.
using System;
using System.IO;
using System.Net.Http;
using Newtonsoft.Json.Linq;
public class CPHInline
{
// Tracks the version of this script for troubleshooting and support.
private const string VERSION = "1.0.0";
// Base URL for the endpoint that generates an AI image.
private const string BASE_URL_GENERATE = "https://api-generate-image-v1.potatohotdog.com/";
// Tracks the next numbered log argument.
private int logIndex = 0;
// Reuses one HTTP client for all API requests.
private static readonly HttpClient httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
// Adds a log message as a numbered action argument.
private void Log(string message)
{
// Stores the message under a numbered key.
CPH.SetArgument($"potatoLog{logIndex}", message);
// Advances the log index for the next message.
logIndex++;
// Keeps the total log count in sync.
CPH.SetArgument("potatoLogCount", logIndex);
}
// Ensures a required action argument exists and is not empty.
private bool RequireArgument(string name, out string value)
{
// Fails when the argument is missing or blank.
if (!CPH.TryGetArg(name, out value) || string.IsNullOrWhiteSpace(value))
{
value = "";
return false;
}
return true;
}
// URL-encodes a value so it can safely be used in a query string.
private string EncodeUrlValue(string value)
{
// Escapes reserved characters for safe use in a URL.
return Uri.EscapeDataString(value);
}
// Performs an HTTP GET request and returns the response body.
private string HttpGet(string url)
{
try
{
Log($"GET {url}");
// Sends the HTTP request synchronously.
HttpResponseMessage response =
httpClient.GetAsync(url).GetAwaiter().GetResult();
// Reads the complete response body.
string responseBody =
response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Log($"HTTP status: {(int)response.StatusCode} {response.ReasonPhrase}");
// Stops when the server returns an unsuccessful status code.
if (!response.IsSuccessStatusCode)
{
Log($"Error: HTTP request failed with status {(int)response.StatusCode} {response.ReasonPhrase}.");
// Logs the server error response when available.
if (!string.IsNullOrWhiteSpace(responseBody))
{
Log($"Error response: {responseBody}");
}
return null;
}
Log($"Received {responseBody.Length} characters.");
return responseBody;
}
catch (Exception exception)
{
// Logs network, timeout, and other HTTP-related exceptions.
Log($"Error: HTTP request failed: {exception.Message}");
return null;
}
}
// Downloads a file from a URL and saves it to disk.
private bool DownloadFile(string url, string destinationPath)
{
try
{
Log($"Downloading image from \"{url}\".");
Log($"Saving image to \"{destinationPath}\".");
// Sends the image download request.
HttpResponseMessage response =
httpClient.GetAsync(url).GetAwaiter().GetResult();
Log($"Image download HTTP status: {(int)response.StatusCode} {response.ReasonPhrase}");
// Reads the complete image response into memory.
byte[] fileData = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
// Stops when the image server returns an unsuccessful status.
if (!response.IsSuccessStatusCode)
{
Log($"Error: Image download failed with status {(int)response.StatusCode} {response.ReasonPhrase}.");
return false;
}
// Rejects an empty image response.
if (fileData == null || fileData.Length == 0)
{
Log("Error: The downloaded image file is empty.");
return false;
}
// Writes the downloaded image bytes to the destination file.
File.WriteAllBytes(destinationPath, fileData);
Log($"Downloaded {fileData.Length} image bytes.");
// Confirms that the image file now exists on disk.
if (!File.Exists(destinationPath))
{
Log("Error: The image download completed, but the file does not exist on disk.");
return false;
}
// Reports the final file size from disk.
FileInfo fileInfo = new FileInfo(destinationPath);
Log($"Image file saved successfully. File size: {fileInfo.Length} bytes.");
return true;
}
catch (Exception exception)
{
// Logs download, filesystem, and permission errors.
Log($"Error: Could not download the image file: {exception.Message}");
return false;
}
}
// Extracts a required string value from a JSON response.
private bool TryGetJsonString(string json, string propertyName, out string value)
{
value = "";
try
{
// Parses the response body as a JSON object.
JObject jsonObject = JObject.Parse(json);
// Retrieves the requested JSON property.
JToken property = jsonObject[propertyName];
if (property == null)
{
Log($"Error: JSON response does not contain \"{propertyName}\".");
return false;
}
value = property.ToString();
// Rejects empty JSON string values.
if (string.IsNullOrWhiteSpace(value))
{
Log($"Error: JSON property \"{propertyName}\" is empty.");
value = "";
return false;
}
return true;
}
catch (Exception exception)
{
// Logs malformed JSON and property conversion errors.
Log($"Error: Could not parse JSON property \"{propertyName}\": {exception.Message}");
return false;
}
}
// Creates and returns the dedicated PotatoImage folder.
private string GetImageFolder()
{
// Uses Streamer.bot's application directory instead of the process working directory.
string streamerBotFolder = AppDomain.CurrentDomain.BaseDirectory;
// Places generated image files inside a dedicated subfolder.
string imageFolder = Path.Combine(streamerBotFolder, "temp_potatohotdog_images");
Log($"Streamer.bot folder: \"{streamerBotFolder}\".");
Log($"Image folder: \"{imageFolder}\".");
// Creates the image folder if it does not already exist.
if (!Directory.Exists(imageFolder))
{
Log("Image folder does not exist. Creating it...");
Directory.CreateDirectory(imageFolder);
Log("Image folder created.");
}
else
{
Log("Image folder already exists.");
}
return imageFolder;
}
// Runs the main PotatoHotdog AI image action.
public bool Execute()
{
// Resets log numbering for this execution.
logIndex = 0;
CPH.SetArgument("potatoLogCount", 0);
Log("Starting script...");
// Logs the script version so runs can be tracked over time.
Log($"Version: {VERSION}");
// Retrieves the PotatoHotdog API key.
if (!RequireArgument("potatoKey", out string potatoKey))
{
Log("Error: potatoKey variable is missing. Please login to potatohotdog.com, generate an API key, create a set variable action and save the variable \"potatoKey\" with your key.");
return false;
}
// Retrieves the prompt instructions describing the image to generate.
if (!RequireArgument("potatoInstructions", out string potatoInstructions))
{
Log("Error: potatoInstructions variable is missing. Create a set variable action and save the variable \"potatoInstructions\" with a description of the image you want generated.");
return false;
}
// Logs the non-secret input values.
Log("potatoKey was provided.");
Log($"potatoInstructions=\"{potatoInstructions}\"");
// Builds the API URL used to generate the AI image.
string generateUrl = BASE_URL_GENERATE + "?key=" + EncodeUrlValue(potatoKey) + "&instructions=" + EncodeUrlValue(potatoInstructions);
// Requests generation of the AI image.
string generateJson = HttpGet(generateUrl);
if (generateJson == null)
{
Log("Error: Failed to generate the AI image.");
return false;
}
Log($"Generate response: {generateJson}");
// Extracts the generated item's ID, reused below as the local filename.
if (!TryGetJsonString(generateJson, "itemID", out string itemID))
{
Log("Error: Could not retrieve itemID from the generate response.");
return false;
}
Log($"itemID=\"{itemID}\"");
// Logs the generation duration when present, purely for troubleshooting.
if (TryGetJsonString(generateJson, "duration", out string duration))
{
Log($"duration={duration} second(s)");
}
// Extracts the generated image URL from the response.
if (!TryGetJsonString(generateJson, "imageURL", out string imageURL))
{
Log("Error: Could not retrieve imageURL from the generate response.");
return false;
}
Log($"imageURL=\"{imageURL}\"");
string imageFolder;
try
{
// Resolves and creates the dedicated PotatoImage directory.
imageFolder = GetImageFolder();
}
catch (Exception exception)
{
Log($"Error: Could not create or access the image folder: {exception.Message}");
return false;
}
// Reuses the item's own ID as the local filename instead of minting a new one.
string imageFileName = $"{itemID}.png";
// Combines the image folder and unique filename.
string imageFilePath = Path.Combine(imageFolder, imageFileName);
Log($"Generated image filename: \"{imageFileName}\".");
Log($"Local image path: \"{imageFilePath}\".");
// Downloads the generated image file to the PotatoImage folder.
if (!DownloadFile(imageURL, imageFilePath))
{
Log("Error: Failed to download the generated AI image.");
return false;
}
// Stores the remote image URL as a final action argument.
CPH.SetArgument("potatoGeneratedImageURL", imageURL);
// Stores the absolute local file path as a final action argument.
CPH.SetArgument("potatoGeneratedImageFile", imageFilePath);
Log($"potatoGeneratedImageURL=\"{imageURL}\"");
Log($"potatoGeneratedImageFile=\"{imageFilePath}\"");
Log("Finished.");
return true;
}
}
Sau khi dán script, nhớ nhấp vào Find Refs và Compile và đảm bảo nó biên dịch thành công. Lưu hành động, rồi kích hoạt nó bằng tay hoặc gán nó cho một lệnh hoặc phần thưởng điểm kênh để kiểm tra xem hình ảnh đã được tạo ra chính xác hay chưa.
Kiểm tra hình ảnh đã tải xuống
Sau khi hành động đã chạy, bạn có thể kiểm tra các biến hành động thông qua Lịch sử Hành động để xác nhận rằng mọi thứ đã hoạt động như mong đợi.
Ở đó bạn sẽ tìm thấy đường dẫn địa phương nơi hình ảnh được tạo ra đã được lưu trên đĩa, nằm trong thư mục temp_potatohotdog_images thuộc thư mục cài đặt Streamer.bot của bạn.
Mở thư mục đó để xem hình đã tải xuống.
Khắc phục sự cố
Nếu script không hoạt động như mong đợi, cách tốt nhất để khắc phục sự cố là kiểm tra nhật ký hoạt động.
Bạn có thể làm điều này bằng cách xem các biến Action của bạn thông qua Lịch sử Action.
Hướng dẫn này lần đầu được công bố vào ngày 2026-07-19 và lần chỉnh sửa gần đây nhất là ngày 2026-07-19.
Các Hướng Dẫn Tương Tự Khác
Dưới đây là một số hướng dẫn cách làm khác có thể hữu ích cho bạn.