Skip to content

Workflow Utilities Reference

erlc-api.py 2.3 adds workflow helpers for dashboards, Discord bots, and multi-server tools. These modules are still lazy: import them explicitly only when needed.

Location

Import:

from erlc_api.location import LocationTools, MapRenderer

Use LocationTools(data) with players, emergency calls, raw dictionaries, or a ServerBundle.

bundle = await api.server(players=True, emergency_calls=True)
tools = LocationTools(bundle)

nearest = tools.nearest_players_to_call(bundle.emergency_calls[0], limit=3)
near_postal = tools.by_postal("218")
map_url = tools.official_map_url(season="fall", layer="postals")

Core methods:

Method Purpose
point(value, z=None) Convert a model, mapping, position list, or x/z pair into Coordinate.
distance(a, b) Euclidean distance between two coordinates.
nearest(origin, items=None, limit=1, max_distance=None) Sorted LocationMatch results.
within_radius(origin, radius, items=None) All location-capable items inside a radius.
by_postal(postal_code) / by_street(street_name) Match v2 player location labels.
official_map_url(season="fall", layer="postals") PRC official map image URL.

Map overlays require Pillow:

pip install "erlc-api.py[location]"

MapRenderer().render_points(...) requires either bounds= or transform= because the official API gives game X/Z coordinates, not a universal pixel calibration.

Vehicle Tools

Import:

from erlc_api.vehicles import PlayerVehicleBundle, VehicleTools

VehicleTools understands the typed vehicle catalog, PRC vehicle names, owners, plates, colors, and textures.

bundle = await api.server(players=True, vehicles=True)
tools = VehicleTools(bundle.vehicles)

prestige = tools.prestige()
duplicates = tools.duplicate_plates()
avi_vehicles = tools.by_owner("Avi")

joined = bundle.player_vehicles
if joined is not None:
    player_view = joined.player("Avi")
    owned = player_view.vehicles if player_view else []

Core methods:

Method Purpose
by_owner(query) Match owner name or user ID.
by_team(players=...) Group vehicles by owner team using player data.
by_color(color) / by_texture(texture) Match visual attributes.
by_model(query) / by_name(query) Catalog-aware model/name search.
find_plate(plate) / duplicate_plates() Normalize and inspect plates.
abandoned(online_players) Vehicles whose owner is not currently online.
primary() / secondary() / prestige() Catalog classification helpers.
summary() Count by model, color, owner, and classification.

See Vehicle Tools for parser and catalog details.

Emergency Calls

Import:

from erlc_api.emergency import EmergencyCallTools

Use these helpers when building dispatch dashboards or responder bots.

bundle = await api.server(players=True, emergency_calls=True)
calls = EmergencyCallTools(bundle.emergency_calls)

unresponded = calls.unresponded()
nearest = calls.nearest_to(bundle.players[0])
summary = calls.summary()

Core methods:

Method Purpose
active() Calls that are still active in the fetched payload.
unresponded() Calls without assigned/responding player IDs.
by_team(team) Police, sheriff, fire, EMS, or other team grouping.
nearest_to(player_or_location) Sort calls by distance from a player/location.
nearest_players_to_call(call, players=...) Find responders near a call.
summary() Count total, active, unresponded, and by team.

See Emergency Calls for call-specific examples.

Bundle Presets

Import:

from erlc_api.bundle import AsyncBundle, Bundle, BundleRequest, register_preset

Built-in presets:

Preset Includes
minimal Server info only.
players Players and queue.
dashboard Players, staff, queue, vehicles, emergency calls.
logs Join, kill, command, and mod logs.
ops Operational bot state.
all Every v2 include.

Examples:

bundle = await AsyncBundle(api).dashboard()
ops = BundleRequest.preset("dashboard").include("command_logs")
bundle = await AsyncBundle(api).fetch(ops)

register_preset("dispatch", ["players", "emergency_calls", "vehicles"])
dispatch = await AsyncBundle(api).fetch("dispatch")

Rules

Import:

from erlc_api.rules import RuleEngine, AsyncRuleEngine, Conditions

Rules evaluate data and return RuleMatch objects. They do not run PRC commands.

engine = RuleEngine()
engine.add(
    "queue-active",
    Conditions.queue_length(at_least=1),
    severity="info",
    message="Queue is active",
)

matches = engine.evaluate(bundle)

Condition helpers cover player count, queue length, staff count, vehicle count, command names/prefixes, emergency-call age, and status severities.

Multi Server

Import:

from erlc_api.multiserver import AsyncMultiServer, MultiServer, ServerRef

Use named server refs so every result remains identifiable.

servers = [
    ServerRef("main", "main-server-key"),
    ServerRef("training", "training-server-key"),
]

manager = AsyncMultiServer(api, servers, concurrency=3)
results = await manager.status()
summary = await manager.aggregate()

Failures are collected per server by default. call(...) only allows read-only client methods; command broadcasting must stay explicit in user code.

Discord Tools

Import:

from erlc_api.discord_tools import DiscordFormatter, DiscordEmbed, DiscordMessage

These helpers return plain dictionaries compatible with most Discord libraries. They do not import discord.py.

from erlc_api.status import StatusBuilder

status = StatusBuilder(bundle).build()
payload = DiscordFormatter().server_status(status).to_dict()
await channel.send(**payload)

Text is clipped to Discord limits and @everyone / @here mentions are made safe.

For the full method reference, see Formatting, Analytics, and Export.

Diagnostics

Import:

from erlc_api.diagnostics import diagnose_error, diagnose_status, diagnose_command_result

Diagnostics turn wrapper objects into user-facing messages.

try:
    await api.players()
except Exception as exc:
    diagnostics = diagnose_error(exc)
    print(diagnostics.to_dict())

Use diagnostics for logs, bot replies, and dashboards; keep catching typed exceptions for control flow.

Cache

Import:

from erlc_api.cache import AsyncCachedClient, CachedClient, MemoryCache

The default cache is memory-only TTL. It caches read endpoints only and never caches command(...).

cached = AsyncCachedClient(api, ttl_s=5)

players = await cached.players()
again = await cached.players()  # served from cache for the same arguments

Applications that need custom stores can provide an adapter implementing the documented cache protocol.

Status

Import:

from erlc_api.status import AsyncStatus, Status, StatusBuilder

Status builders produce typed dashboard snapshots:

status = await AsyncStatus(api).get()
print(status.health, status.to_dict())

StatusBuilder(bundle).build() works with data you already fetched, avoiding an extra HTTP call.

Command Flows

Import:

from erlc_api.command_flows import CommandFlowBuilder, CommandTemplate

Flows preview and validate command sequences. They do not execute commands.

warn = CommandTemplate("warn", "warn {target} {reason}")

flow = (
    CommandFlowBuilder("warn-and-pm")
    .template(warn, target="Avi", reason="RDM")
    .step("pm Avi Please review the rules")
    .build()
)

print(flow.preview())

Common mistakes:

  • Expecting rules or command flows to send PRC commands automatically.
  • Using multi-server utilities for command broadcasting.
  • Installing Pillow for geometry-only location helpers; it is only needed for rendered overlays.
  • Caching command results. CachedClient intentionally caches read methods only.

Previous Page: Ops Utilities Reference | Next Page: Formatting, Analytics, and Export