Repository navigation
RU UO Topic http
Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
Главная · C# · IDE · UO API · A–Z · Примеры · Языки
HTTP-запросы и чтение ответа. Код состояния, заголовок и тело ответа — отдельные данные.
| Тема | Команды и пояснения |
|---|---|
| HTTP_Get | UO.HTTP_Get |
| HTTP_Post | UO.HTTP_Post |
| HTTP_Header | UO.HTTP_Header |
| HTTP_Body | UO.HTTP_Body |
| Внешние сообщения | UO.Messenger — Telegram, Discord и Viber |
UO.HTTP_Get · UO.HTTP_Post · UO.HTTP_Header · UO.HTTP_Body
В каждой карточке команды есть несколько самостоятельных сценариев: прямой код, функции и классы. Ниже приведены отдельные короткие сценарии; ограничения и полный разбор находятся в карточках.
Параметры, результат и все сценарии
UO.HTTP_Get("https://example.com/csharp-config.json");
try
{
string body = Convert.ToString(UO.HTTP_Body()) ?? "";
using (var document = System.Text.Json.JsonDocument.Parse(body))
{
if (document.RootElement.TryGetProperty("minMana", out var value) &&
value.TryGetInt32(out int threshold) && threshold is >= 0 and <= 100)
return threshold;
}
}
catch (System.Text.Json.JsonException)
{
UO.Print("Response is not a valid configuration; using the default.");
}
return 30;Параметры, результат и все сценарии
UO.HTTP_Get("https://example.com/C#-permission");
var permission = TrimText(UO.HTTP_Body());
return Truth(permission == "allow");
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.HTTP_Body();Читает текст последнего HTTP-ответа этого скрипта. Например, после HTTP_Get можно разобрать JSON с настройками или сравнить ответ своего сервиса с ожидаемой строкой. Само чтение не обращается в сеть.
Параметров нет.
string — сохранённый текст. До первого запроса в этом runtime, после GET в поток и после сетевой ошибки — пустая строка. Пустое тело также бывает у нормального ответа, например HTTP 204. Не сравнивайте результат с true/false как признак доставки.
- Последний ответ хранится только в текущем runtime скрипта. Следующий GET/POST этого runtime заменяет его; другие одновременно работающие скрипты его не меняют. Ответ не сохраняется в профиль. Если он нужен после следующего запроса, заранее скопируйте тело и заголовки в переменные.
- GET/POST ждут ответа в потоке выполняющегося скрипта. Общий срок запроса и чтения тела — 20 секунд. Остановка скрипта отменяет ожидание; HTTP_Body/HTTP_Header ничего не ждут. Всё тело загружается в память даже при передаче FileStream; это не потоковая загрузка больших файлов.
- Текст декодируется по charset ответа, при отсутствии/неизвестной кодировке — UTF-8. Ответы HTTP 4xx/5xx тоже читаются. При сетевой ошибке или таймауте клиент пишет ошибку, тело и заголовки становятся пустыми; Try/Catch не заменяет проверку ожидаемого ответа. Отмена скрипта распространяется дальше. Ошибка локальной записи файла или неправильного аргумента может быть перехвачена Try/Catch.
- HTTP_Body только читает последний буфер: не повторяет запрос, не очищает ответ и не выдаёт данные другого скрипта. После двоичного GET используйте MemoryStream.ToArray() либо сохранённый файл, а не этот текстовый буфер.
Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.
Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.
Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.
UO.HTTP_Get("https://example.com/");
object? result = UO.HTTP_Body();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));Параметры и ход выполнения:
- Сервис должен вернуть allow. Другой ответ, включая пустой, даёт false. Пример лишь вычисляет разрешение; не запускает движение или бой.
- Адреса example.com иллюстративные и не предоставляют такой сервис. Замените их своими; запуск примера делает реальный HTTP-запрос. Игровых действий пример не выполняет.
UO.HTTP_Get("https://example.com/C#-permission");
var permission = TrimText(UO.HTTP_Body());
return Truth(permission == "allow");
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();Параметры и ход выполнения:
- Сначала сохраняется название маршрута, затем запрашивается описание. Без routeName второй GET заменил бы первый ответ.
- Адреса example.com иллюстративные и не предоставляют такой сервис. Замените их своими; запуск примера делает реальный HTTP-запрос. Игровых действий пример не выполняет.
return RunScenario();
object? RunScenario()
{
UO.HTTP_Get("https://example.com/route-name");
var routeName = UO.HTTP_Body();
UO.HTTP_Get("https://example.com/route-description");
return routeName + ": " + UO.HTTP_Body();
return null;
}Параметры и ход выполнения:
- После GET с System.IO.MemoryStream байты находятся в потоке, а HTTP_Body пуст. Это правило действует и для корректного бинарного ответа; пустой текст не означает отсутствие байтов.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
using (var data = new System.IO.MemoryStream())
{
UO.HTTP_Get("https://example.com/route-data.bin", data);
string body = Convert.ToString(UO.HTTP_Body()) ?? "";
UO.Print($"Binary bytes: {data.Length}; text-body characters: {body.Length}");
return body;
}
return null;
}
}C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.
Параметры: Параметров нет.
Результат: dynamic / Any.
При вызове:
Внутренний код реализации, не самостоятельный скрипт.
case "http_body": return new InjectionValue(httpBody);Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
void UO.HTTP_Get(object? url);
void UO.HTTP_Get(object? url, object? lStream);Загружает данные по HTTP/HTTPS. Подходит для чтения настроек со своего сервиса или получения файла; не управляет игровым подключением и не запускает загруженный код.
-
url— Обязательная string: полный адрес http:// или https://. Замените example.com адресом своего сервиса. Игра и игровой сервер для этого запроса не нужны; используется обычное сетевое подключение процесса, а не игровой прокси профиля. -
lStream— необязательный стандартный .NET System.IO.Stream, доступный для записи: например new System.IO.MemoryStream() или System.IO.File.Create(path). Без него либо с 0/null текст читается HTTP_Body(). Поток получает исходные байты. Для seekable-потока прежнее содержимое обнуляется, а позиция до и после записи становится 0; non-seekable-поток получает запись в текущую позицию. Создание FileStream через File.Create обнуляет файл сразу; родительский каталог должен существовать. Неподходящий аргумент или поток только для чтения отклоняется до запроса.
void — возвращаемого значения нет. Вызывайте отдельной строкой; результат нельзя использовать как bool, ID или количество байтов. Текст после обычного запроса читается HTTP_Body(), двоичные данные — из переданного потока.
- Последний ответ хранится только в текущем runtime скрипта. Следующий GET/POST этого runtime заменяет его; другие одновременно работающие скрипты его не меняют. Ответ не сохраняется в профиль. Если он нужен после следующего запроса, заранее скопируйте тело и заголовки в переменные.
- GET/POST ждут ответа в потоке выполняющегося скрипта. Общий срок запроса и чтения тела — 20 секунд. Остановка скрипта отменяет ожидание; HTTP_Body/HTTP_Header ничего не ждут. Всё тело загружается в память даже при передаче FileStream; это не потоковая загрузка больших файлов.
- Текст декодируется по charset ответа, при отсутствии/неизвестной кодировке — UTF-8. Ответы HTTP 4xx/5xx тоже читаются. При сетевой ошибке или таймауте клиент пишет ошибку, тело и заголовки становятся пустыми; Try/Catch не заменяет проверку ожидаемого ответа. Отмена скрипта распространяется дальше. Ошибка локальной записи файла или неправильного аргумента может быть перехвачена Try/Catch.
- GET без потока обновляет текст и заголовки. GET с потоком записывает байты, очищает HTTP_Body и сохраняет заголовки. Даже пустой ответ/сетевая ошибка заменяют содержимое потока пустыми данными: для сохранения прежнего файла сначала скачайте в MemoryStream и проверьте данные.
Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.
Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.
Команда вызывается отдельной строкой. Сообщение перед ней показывает начало вызова, а не подтверждение действия сервера.
UO.Print("Calling UO.HTTP_Get");
UO.HTTP_Get("https://example.com/");Параметры и ход выполнения:
- example.com — демонстрационный адрес; замените его своим. System.Text.Json читает тело ответа и принимает только целое minMana от 0 до 100. Пустой/неправильный JSON даёт значение по умолчанию 30.
UO.HTTP_Get("https://example.com/csharp-config.json");
try
{
string body = Convert.ToString(UO.HTTP_Body()) ?? "";
using (var document = System.Text.Json.JsonDocument.Parse(body))
{
if (document.RootElement.TryGetProperty("minMana", out var value) &&
value.TryGetInt32(out int threshold) && threshold is >= 0 and <= 100)
return threshold;
}
}
catch (System.Text.Json.JsonException)
{
UO.Print("Response is not a valid configuration; using the default.");
}
return 30;Параметры и ход выполнения:
- Передаётся стандартный System.IO.MemoryStream. Он получает оригинальные байты ответа; HTTP_Body становится пустым. Существующий download.bin заменяется только после получения непустых данных. Проверка длины не доказывает правильность содержимого.
return RunScenario();
object? RunScenario()
{
using (var data = new System.IO.MemoryStream())
{
UO.HTTP_Get("https://example.com/route-data.bin", data);
if (data.Length == 0)
{
UO.Print("The response is empty; no file was written.");
return false;
}
System.IO.File.WriteAllBytes("download.bin", data.ToArray());
return true;
}
return null;
}Параметры и ход выполнения:
- System.IO.File.Create создаёт или сразу обнуляет файл; GET записывает исходные байты. При пустом ответе файл останется пустым. Для сохранения предыдущей версии сначала используйте MemoryStream, как в предыдущем сценарии. using закрывает поток.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
using (var output = System.IO.File.Create("http-download.bin"))
{
UO.HTTP_Get("https://example.com/route-data.bin", output);
}
return UO.HTTP_Header();
return null;
}
}C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.
Параметры: url (Any)
Результат: void / Unit.
При вызове:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.HttpRequest:bridge.HttpRequest("GET", Text(0), string.Empty). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Text:Text(0). -
InjectionScript.Runtime.ObjectTypes.MemoryStreamObject.ReplaceBytes:memoryStream.ReplaceBytes(raw).
Внутренний код реализации, не самостоятельный скрипт.
case "http_get":
{
bool hasStream = arguments.Length > 1
&& !(arguments[1].Kind == InjectionValueKind.Integer && arguments[1].Integer == 0)
&& arguments[1].Kind != InjectionValueKind.Unit;
// Validate before network side effects and before changing the last response.
if (hasStream && !(arguments[1].Object is MemoryStreamObject))
throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream, 0, or omitted.");
string[] response = bridge.HttpRequest("GET", Text(0), string.Empty);
httpHeader = response.Length > 1 ? response[1] : string.Empty;
httpBody = hasStream ? string.Empty : response.Length > 0 ? response[0] : string.Empty;
if (!hasStream)
{
return InjectionValue.Unit;
}
byte[] raw = response.Length > 3 && !string.IsNullOrEmpty(response[3])
? Convert.FromBase64String(response[3])
: Array.Empty<byte>();
if (arguments[1].Object is MemoryStreamObject memoryStream)
memoryStream.ReplaceBytes(raw);
else
throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream.");
// Stream overloads keep the response as bytes, not cached text.
return InjectionValue.Unit;
}Параметры: url (Any), lStream (Any)
Результат: void / Unit.
При вызове:
-
ClassicUO.CSharp.CSharpScriptGlobals.CSharpScriptGlobals. -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.HttpRequest:bridge.HttpRequest("GET", Text(0), string.Empty). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Text:Text(0). -
InjectionScript.Runtime.ObjectTypes.MemoryStreamObject.ReplaceBytes:memoryStream.ReplaceBytes(raw).
Внутренний код реализации, не самостоятельный скрипт.
if (name == "HTTP_Get" && arguments.Length == 2 && arguments[1] is Stream stream)
{
if (!stream.CanWrite) throw new ArgumentException("HTTP_Get requires a writable .NET stream.");
// Adapt the existing binary response path without fetching the
// response twice or converting its bytes through text encoding.
var type = typeof(InjectionValue).Assembly.GetType("InjectionScript.Runtime.ObjectTypes.MemoryStreamObject", throwOnError: true)!;
using var native = (IDisposable)Activator.CreateInstance(type, nonPublic: true)!;
value = Invoke(name, [arguments[0], native]);
var bytes = (InjectionValue)type.GetMethod("ToArray")!.Invoke(native, null)!;
byte[] payload = bytes.Array.Select(item => checked((byte)item.Integer)).ToArray();
if (stream.CanSeek) { stream.SetLength(0); stream.Position = 0; }
stream.Write(payload);
if (stream.CanSeek) stream.Position = 0;
}
else value = Invoke(name, arguments);Внутренний код реализации, не самостоятельный скрипт.
case "http_get":
{
bool hasStream = arguments.Length > 1
&& !(arguments[1].Kind == InjectionValueKind.Integer && arguments[1].Integer == 0)
&& arguments[1].Kind != InjectionValueKind.Unit;
// Validate before network side effects and before changing the last response.
if (hasStream && !(arguments[1].Object is MemoryStreamObject))
throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream, 0, or omitted.");
string[] response = bridge.HttpRequest("GET", Text(0), string.Empty);
httpHeader = response.Length > 1 ? response[1] : string.Empty;
httpBody = hasStream ? string.Empty : response.Length > 0 ? response[0] : string.Empty;
if (!hasStream)
{
return InjectionValue.Unit;
}
byte[] raw = response.Length > 3 && !string.IsNullOrEmpty(response[3])
? Convert.FromBase64String(response[3])
: Array.Empty<byte>();
if (arguments[1].Object is MemoryStreamObject memoryStream)
memoryStream.ReplaceBytes(raw);
else
throw new NativeSubrutineException("UO.HTTP_Get: LStream must be a writable System.IO.Stream.");
// Stream overloads keep the response as bytes, not cached text.
return InjectionValue.Unit;
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.HTTP_Header();Читает заголовки последнего HTTP-ответа этого скрипта: например, Content-Type, чтобы узнать заявленный формат данных. Это не тело ответа, не заголовки запроса и не числовой код HTTP.
Параметров нет.
string — строки вида Имя: значение, разделённые CRLF. До первого запроса и после сетевой ошибки — пустая строка. Строки HTTP/1.1 200 OK здесь нет; код состояния не представлен отдельной командой. Порядок и регистр имён не гарантированы.
- Последний ответ хранится только в текущем runtime скрипта. Следующий GET/POST этого runtime заменяет его; другие одновременно работающие скрипты его не меняют. Ответ не сохраняется в профиль. Если он нужен после следующего запроса, заранее скопируйте тело и заголовки в переменные.
- GET/POST ждут ответа в потоке выполняющегося скрипта. Общий срок запроса и чтения тела — 20 секунд. Остановка скрипта отменяет ожидание; HTTP_Body/HTTP_Header ничего не ждут. Всё тело загружается в память даже при передаче FileStream; это не потоковая загрузка больших файлов.
- Текст декодируется по charset ответа, при отсутствии/неизвестной кодировке — UTF-8. Ответы HTTP 4xx/5xx тоже читаются. При сетевой ошибке или таймауте клиент пишет ошибку, тело и заголовки становятся пустыми; Try/Catch не заменяет проверку ожидаемого ответа. Отмена скрипта распространяется дальше. Ошибка локальной записи файла или неправильного аргумента может быть перехвачена Try/Catch.
- HTTP_Header объединяет заголовки ответа и его содержимого. После двоичного GET они тоже доступны. Проверять имя следует без учёта регистра. Content-Type — заявление сервера, а не проверка содержимого; не выполняйте загруженный текст как код.
Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.
Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.
Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.
UO.HTTP_Get("https://example.com/");
object? result = UO.HTTP_Header();
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));Параметры и ход выполнения:
- Заголовки разбираются по строкам. Имя Content-Type сравнивается без учёта регистра; при отсутствии строки возвращается пустая строка.
UO.HTTP_Get("https://example.com/csharp-status");
return ReadHeader(Convert.ToString(UO.HTTP_Header()) ?? "", "Content-Type");
string ReadHeader(string headers, string name)
{
foreach (string line in headers.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries))
{
int separator = line.IndexOf(':');
if (separator > 0 && line[..separator].Trim().Equals(name, StringComparison.OrdinalIgnoreCase))
return line[(separator + 1)..].Trim();
}
return "";
}Параметры и ход выполнения:
- Проверяется локально сохранённый текст заголовков. Content-Type сам по себе не гарантирует правильность JSON; содержимое отдельно проверяйте System.Text.Json.
return RunScenario();
object? RunScenario()
{
UO.HTTP_Get("https://example.com/csharp-status");
string headers = Convert.ToString(UO.HTTP_Header()) ?? "";
return headers.Contains("application/json", StringComparison.OrdinalIgnoreCase);
return null;
}Параметры и ход выполнения:
- GET направляет исходные байты в System.IO.MemoryStream. Заголовки последнего ответа по-прежнему читаются HTTP_Header; текстовый HTTP_Body для этой формы пуст. В файл сохраняются только заголовки.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
using (var data = new System.IO.MemoryStream())
{
UO.HTTP_Get("https://example.com/route-data.bin", data);
string headers = Convert.ToString(UO.HTTP_Header()) ?? "";
System.IO.File.WriteAllText("http-response-headers.txt", headers);
UO.Print($"Saved response headers; downloaded {data.Length} binary bytes.");
return headers;
}
return null;
}
}C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.
Параметры: Параметров нет.
Результат: dynamic / Any.
При вызове:
Внутренний код реализации, не самостоятельный скрипт.
case "http_header": return new InjectionValue(httpHeader);Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.HTTP_Post(object? url, object? postData);Отправляет текст своему HTTP-сервису методом POST и возвращает текст ответа. Например, скрипт может передать отчёт об остановке маршрута. Это не готовая команда Telegram/Discord: их протокол, авторизация и формат запроса требуют отдельной поддержки.
-
url— Обязательная string: полный адрес http:// или https://. Замените example.com адресом своего сервиса. Игра и игровой сервер для этого запроса не нужны; используется обычное сетевое подключение процесса, а не игровой прокси профиля. -
postData— Обязательная string с телом запроса. Отправляется UTF-8 с Content-Type: text/plain; charset=utf-8. Нет автоматического кодирования формы и переключения на application/json. System.Text.Json.JsonSerializer.Serialize лишь формирует текст: сервер примера должен принимать JSON именно как text/plain. Настроить произвольные заголовки этими командами нельзя.
string — текст ответа; он же доступен через HTTP_Body(). Это не bool и не код HTTP. Пустая строка не доказывает ошибку, а непустая — успех операции на сервере.
- Последний ответ хранится только в текущем runtime скрипта. Следующий GET/POST этого runtime заменяет его; другие одновременно работающие скрипты его не меняют. Ответ не сохраняется в профиль. Если он нужен после следующего запроса, заранее скопируйте тело и заголовки в переменные.
- GET/POST ждут ответа в потоке выполняющегося скрипта. Общий срок запроса и чтения тела — 20 секунд. Остановка скрипта отменяет ожидание; HTTP_Body/HTTP_Header ничего не ждут. Всё тело загружается в память даже при передаче FileStream; это не потоковая загрузка больших файлов.
- Текст декодируется по charset ответа, при отсутствии/неизвестной кодировке — UTF-8. Ответы HTTP 4xx/5xx тоже читаются. При сетевой ошибке или таймауте клиент пишет ошибку, тело и заголовки становятся пустыми; Try/Catch не заменяет проверку ожидаемого ответа. Отмена скрипта распространяется дальше. Ошибка локальной записи файла или неправильного аргумента может быть перехвачена Try/Catch.
- POST обновляет тело и заголовки и возвращает то же тело строкой. Не повторяйте POST автоматически только из-за пустого ответа: сервер мог уже выполнить действие. Проверка accepted/status в примерах — договорённость с вашим сервисом, не встроенный признак HTTP-успеха.
Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.
Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.
Результат одного вызова выводится в журнал. Число не преобразуется в bool; для массива показано количество элементов.
object? result = UO.HTTP_Post("https://example.com/", "Hello from C#");
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));Параметры и ход выполнения:
- Сервис принимает текст Route stopped и по договорённости отвечает accepted. Main возвращает true только при таком тексте; это проверка протокола вашего сервиса, не проверка HTTP-кода.
- Адреса example.com иллюстративные и не предоставляют такой сервис. Замените их своими; запуск примера делает реальный HTTP-запрос. Игровых действий пример не выполняет.
var reply = UO.HTTP_Post("https://example.com/C#-event", "Route stopped");
return Truth(TrimText(reply) == "accepted");
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();Параметры и ход выполнения:
- System.Text.Json.JsonSerializer экранирует строки словаря. Значение status читается после проверки JSON; демонстрационный адрес замените своим.
return RunScenario();
object? RunScenario()
{
var report = new Dictionary<string, string>
{
["event"] = "route-stopped",
["text"] = "line one\nline two"
};
string payload = System.Text.Json.JsonSerializer.Serialize(report);
string reply = Convert.ToString(UO.HTTP_Post("https://example.com/csharp-report", payload)) ?? "";
try
{
using (var document = System.Text.Json.JsonDocument.Parse(reply))
{
return document.RootElement.TryGetProperty("status", out var value) ? value.GetString() : "unknown";
}
}
catch (System.Text.Json.JsonException)
{
return "invalid response";
}
return null;
}Параметры и ход выполнения:
- firstReply и firstHeaders сохраняют ответ POST. Последующий GET заменяет внутренние буферы, но локальные переменные по-прежнему содержат первый ответ.
- Адреса example.com иллюстративные и не предоставляют такой сервис. Замените их своими; запуск примера делает реальный HTTP-запрос. Игровых действий пример не выполняет.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
var firstReply = UO.HTTP_Post("https://example.com/C#-event", "Route stopped");
var firstHeaders = UO.HTTP_Header();
UO.HTTP_Get("https://example.com/C#-status");
return firstReply + "|" + firstHeaders;
return null;
}
}C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.
Параметры: url (Any), postData (Any)
Результат: dynamic / Any.
При вызове:
-
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility. -
ClassicUO.Game.Managers.ClassicUOInjectionApiBridge.HttpRequest:bridge.HttpRequest("POST", Text(0), Text(1)). -
InjectionScript.Runtime.InjectionApiUO.ExecuteClientApiCompatibility::Text:Text(0).
Внутренний код реализации, не самостоятельный скрипт.
case "http_post":
{
string[] response = bridge.HttpRequest("POST", Text(0), Text(1));
httpBody = response.Length > 0 ? response[0] : string.Empty;
httpHeader = response.Length > 1 ? response[1] : string.Empty;
return new InjectionValue(httpBody);
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.Messenger(object? provider);Создаёт объект для текстовых уведомлений и приёма сообщений Telegram, Discord или Viber. Например, скрипт может сообщить об окончании сбора ресурсов или проверить запрос остановки от заранее указанного пользователя. Создание объекта ещё не подключает бота и ничего не отправляет.
-
provider— string: "telegram", "discord" или "viber"; регистр не важен. Другие значения вызывают ошибку.
Object Messenger. Это не bool и не ID. ID сообщений, отправителей и чатов в этом объекте всегда string: сравнивайте их со строками, не преобразуйте через Convert.ToInt32/Convert.ToDouble.
-
Provider() → string;Connected() → 1/0(true/false): поставщик и результат последней проверки авторизации. Connected не гарантирует доступность Интернета в эту секунду. -
Connect(token) → null: проверяет токен бота;ConnectFromFile(path) → null: читает токен UTF-8 из файла до 1024 байт. Относительный путь считается от папки текущего скрипта. Повторное подключение требует Disconnect. Ошибки можно перехватить Try/Catch. -
Disconnect() → null: прекращает локальный приём, очищает токен и курсоры; объект можно подключить снова.Dispose() → null: закрывает объект окончательно. -
SendMessage(text, recipient) → string: ID принятого сообщения. Параметры — текст и строковый chat ID Telegram, channel ID Discord либо ID подписчика Viber. Лимиты длины: 4096/2000/7000 соответственно; также Viber JSON ≤30000 байт. Пустой текст запрещён. Discord не создаёт упоминания everyone/ролей. -
WatchChannel(channelId) → null: Discord, запоминает текущее последнее сообщение и читает только последующие.WatchChannel(channelId, afterMessageId) → null: начинает после заданного строкового ID; "0" включает историю. До 32 каналов на объект. -
Receive(timeoutSeconds) → Array;Receive(): таймаут 0. Диапазон 0..20 секунд; пустой массив означает отсутствие доступных текстовых сообщений. Telegram использует long polling, Discord опрашивает выбранные каналы, Viber читает очередь webhook. Сетевой запрос может занять до 30 секунд. - Каждый MessengerMessage:
Id(),SenderId(),SenderName(),ChatId(),Text()→ string. У поста Telegram от канала может не быть SenderId. Вложения, нетекстовые события и сообщения Discord от ботов не возвращаются. -
RetryAfter() → int: сколько секунд ещё ждать после ограничения сервиса. До истечения этого срока новые HTTP-запросы не выполняются. Отправка после сбоя не повторяется автоматически: сервер мог уже принять сообщение. -
StartReceiver(localPort, publicHttpsUrl) → int: только Viber; порт 1..65535, возвращает занятый локальный порт. Публичный HTTPS-адрес должен оканчиваться /viber/ и заранее пересылать POST на 127.0.0.1:порт/viber/ с исходным телом и заголовком подписи. Метод регистрирует webhook у Viber. - Нужен токен бота, не пароль личного аккаунта. Telegram с существующим webhook отклоняется без его удаления. Discord требует прав чтения/отправки канала и MESSAGE_CONTENT для доступного текста; Viber отправляет подписавшимся пользователям.
- Receive сохраняет курсор только после разбора всей пачки. Telegram/Discord возвращают до 100 записей за запрос; вызывайте Receive снова для следующих пачек. Discord без второго аргумента WatchChannel пропускает старую историю.
- Viber проверяет HMAC-SHA256 исходного тела. Очередь — 100 сообщений, повторные ID хранятся для последних 2048 доставок; переполнение даёт HTTP 503 для повтора сервисом. Нужен HTTPS-прокси с Content-Length; chunked-запросы на локальном входе отклоняются.
- Токен не записывается в профиль, текст ошибок и историю аргументов Connect. Используйте ConnectFromFile, чтобы не хранить его в переменных скрипта, видимых отладчику. Не публикуйте файлы токенов.
- Создавайте объект внутри процедуры. Не более 32 одновременно; Using/Dispose освобождает его раньше. При завершении, ошибке или остановке скрипта все его messenger-объекты закрываются. Закрытие Viber-приёмника не удаляет webhook у сервиса.
- Это текстовый интерфейс: он не выполняет полученный текст как код и не подключает игровые аккаунты. Условия доверия к отправителю задаёт сам скрипт.
Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.
Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.
dynamic messenger = UO.Messenger("telegram");
UO.Print(messenger.Provider());
messenger.Dispose();Параметры и ход выполнения:
- Положите telegram-token.txt рядом со скриптом, замените 12345678 на свой chat ID. Пример отправляет одно уведомление и возвращает строковый ID; получение ответа сервера не подтверждает прочтение человеком.
- Нужны собственные настройки бота. Примеры проверены с локальными тестовыми ответами; они не отправлялись реальным пользователям.
var bot = UO.Messenger("telegram");
using (var resource = bot)
{
bot.ConnectFromFile("telegram-token.txt");
var messageId = bot.SendMessage("Harvest finished", "12345678");
return messageId;
}Параметры и ход выполнения:
- Укажите свой канал и ID разрешённого отправителя. true означает, что за эту проверку пришёл текст stop от него; сам пример не останавливает движение. Этот результат можно использовать в условии собственного рабочего цикла.
- Нужны собственные настройки бота. Примеры проверены с локальными тестовыми ответами; они не отправлялись реальным пользователям.
return RunScenario();
object? RunScenario()
{
var bot = UO.Messenger("discord");
using (var resource = bot)
{
bot.ConnectFromFile("discord-token.txt");
bot.WatchChannel("123456789012345678");
var messages = bot.Receive(5);
foreach (var message in messages)
{
if (Truth(message.SenderId() == "987654321098765432") && Truth(Convert.ToString(TrimText(message.Text())) == "stop"))
{
return true;
}
}
return false;
}
return null;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
string TrimText(object? value) => (Convert.ToString(value) ?? "").Trim();Параметры и ход выполнения:
- System.Text.Json читает локальные port/publicUrl. Токен хранится в отдельном локальном файле; реальные отправки не выполняются этим примером. Проверяется доверенный SenderId, а не произвольный входящий текст.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
var settingsDocument = System.Text.Json.JsonDocument.Parse(System.IO.File.ReadAllText("viber-settings.json"));
using (settingsDocument)
{
var settings = settingsDocument.RootElement;
var bot = UO.Messenger("viber");
using (var resource = bot)
{
bot.ConnectFromFile("viber-token.txt");
bot.StartReceiver(settings.GetProperty("port").GetInt32(), settings.GetProperty("publicUrl").GetString());
var messages = bot.Receive(5);
foreach (var message in messages)
{
if (Truth(message.SenderId() == "trusted-subscriber-id"))
{
return message.Text();
}
}
return "";
}
}
return null;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# учитывает регистр. Используйте точное имя UO. Числовые флаги сравнивайте с 0; bool используйте только там, где сигнатура возвращает bool.
Параметры: provider (String)
Результат: dynamic / Object.
При вызове:
Внутренний код реализации, не самостоятельный скрипт.
private InjectionObject CreateMessenger(string provider)
{
var owner=NativeScriptResourceScope.Current??throw new StatementFailedException("Create messenger objects inside an active C# script execution.");owner.Checkpoint();
var client=MessengerFactory!=null?MessengerFactory(provider,()=>owner.Cancellation,owner.Checkpoint):new Messaging.MessengerClient(provider,()=>owner.Cancellation,owner.Checkpoint);
var resource=new MessengerObject(client,owner.Forget,file=>Path.GetFullPath(Path.Combine(paths.ScriptPath??string.Empty,file)));owner.Track(resource);return resource;
}ClassicUO — Bad Newbie & C# Scripts IDE