Skip to content

RU UO Topic http

Codex edited this page Oct 12, 2026 · 4 revisions

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

HTTP

Главная · 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

Все команды темы

HTTP Запросы

UO.HTTP_Get · UO.HTTP_Post · UO.HTTP_Header · UO.HTTP_Body

Примеры по теме

В каждой карточке команды есть несколько самостоятельных сценариев: прямой код, функции и классы. Ниже приведены отдельные короткие сценарии; ограничения и полный разбор находятся в карточках.

UO.HTTP_Get

Параметры, результат и все сценарии

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_Body

Параметры, результат и все сценарии

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();

Полное описание команд


UO.HTTP_Body

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C#-сигнатуры

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() либо сохранённый файл, а не этот текстовый буфер.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .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));

1. Разрешить действие только при ожидаемом тексте — прямой скрипт

Параметры и ход выполнения:

  • Сервис должен вернуть 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();

2. Собрать описание из двух ответов — функция сценария

Параметры и ход выполнения:

  • Сначала сохраняется название маршрута, затем запрашивается описание. Без 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;
}

3. Отделить бинарный поток от текстового буфера — класс сценария

Параметры и ход выполнения:

  • После 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.

UO.HTTP_Body() — UO.HTTP_Body()

Параметры: Параметров нет.

Результат: dynamic / Any.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "http_body": return new InjectionValue(httpBody);

UO.HTTP_Get

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C#-сигнатуры

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 и проверьте данные.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

Команда вызывается отдельной строкой. Сообщение перед ней показывает начало вызова, а не подтверждение действия сервера.

UO.Print("Calling UO.HTTP_Get");
UO.HTTP_Get("https://example.com/");

1. Прочитать JSON-конфигурацию с проверкой диапазона — прямой скрипт

Параметры и ход выполнения:

  • 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;

2. Проверить бинарные байты перед сохранением файла — функция сценария

Параметры и ход выполнения:

  • Передаётся стандартный 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;
}

3. Скачать бинарный ответ прямо в FileStream — класс сценария

Параметры и ход выполнения:

  • 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.

UO.HTTP_Get(url) — UO.HTTP_Get(Any)

Параметры: url (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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;

                }

UO.HTTP_Get(url, lStream) — UO.HTTP_Get(Any,Any)

Параметры: url (Any), lStream (Any)

Результат: void / Unit.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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;

                }

UO.HTTP_Header

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C#-сигнатуры

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 — заявление сервера, а не проверка содержимого; не выполняйте загруженный текст как код.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .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));

1. Прочитать заголовок ответа без зависимости от регистра — прямой скрипт

Параметры и ход выполнения:

  • Заголовки разбираются по строкам. Имя 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 "";
}

2. Проверить объявленный JSON Content-Type — функция сценария

Параметры и ход выполнения:

  • Проверяется локально сохранённый текст заголовков. 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;
}

3. Сохранить заголовки бинарного ответа — класс сценария

Параметры и ход выполнения:

  • 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.

UO.HTTP_Header() — UO.HTTP_Header()

Параметры: Параметров нет.

Результат: dynamic / Any.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

case "http_header": return new InjectionValue(httpHeader);

UO.HTTP_Post

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C#-сигнатуры

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-успеха.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .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));

1. Проверить договорённый ответ сервиса — прямой скрипт

Параметры и ход выполнения:

  • Сервис принимает текст 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();

2. Отправить сериализованный JSON и разобрать JSON-ответ — функция сценария

Параметры и ход выполнения:

  • 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;
}

3. Сохранить ответ до следующего запроса — класс сценария

Параметры и ход выполнения:

  • 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.

UO.HTTP_Post(url, postData) — UO.HTTP_Post(Any,Any)

Параметры: url (Any), postData (Any)

Результат: dynamic / Any.

При вызове:

Внутренний код реализации, не самостоятельный скрипт.

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);

                }

UO.Messenger

Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어

C#-сигнатуры

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 у сервиса.
  • Это текстовый интерфейс: он не выполняет полученный текст как код и не подключает игровые аккаунты. Условия доверия к отправителю задаёт сам скрипт.

Самостоятельные C#-скрипты

Каждый пример можно сохранить отдельным файлом .csx и запускать в IDE. Объект UO и функции ожидания предоставляет host текущей игровой сессии. Демонстрационные serial, имена, координаты и адреса замените своими.

Простой пример

Выполните этот код как отдельный файл .csx. Аргументы видны прямо в вызове; демонстрационные имена, serial и адреса замените своими.

dynamic messenger = UO.Messenger("telegram");
UO.Print(messenger.Provider());
messenger.Dispose();

1. Telegram: уведомление после завершения работы — прямой скрипт

Параметры и ход выполнения:

  • Положите 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;
}

2. Discord: запрос остановки от выбранного пользователя — функция сценария

Параметры и ход выполнения:

  • Укажите свой канал и 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();

3. Загрузить настройки приёмника из JSON-файла — класс сценария

Параметры и ход выполнения:

  • 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.

UO.Messenger(provider) — UO.Messenger(String)

Параметры: 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;
        }

Clone this wiki locally