Tornex
View SourceAn Elixir library providing robust API call planning and execution for the Torn City API.
NOTE
Currently, tornex is intended to work on single-node deployments. Multi-node deployments will experience issues with ratelimiting.
Features
- Efficient API ratelimiting and planning using buckets and prioritized requests
:telemetry
-based logging (with included support for PromEx)- Support for APIv1 and APIv2
Roadmap
- [ ] Per-IP/node global ratelimit
- [ ] Combine similar queries to reduce throughput
- [ ] Multi-node support
- [ ] APIv2 struct coalescing using torngen
- [ ] APIv2 mocking server using torngen
Installation
Once available in Hex, the library can be installed
by adding tornex
to your list of dependencies in mix.exs
:
def deps do
[
{:tornex, "~> 0.2.0"}
]
end
For latest changes, you can also install the library directly from GitHub:
def deps do
[
{:tornex, github: "Tornium/tornex"}
]
end
Usage
Add the supervisor Tornex.Scheduler.Supervisor
to your application supervisor. If you are using the default telemetry handler using Tornex.Telemetry.attach_default_handler
, make sure to start the handler before starting the supervisor.
APIv1
Create a Tornex.Query
struct containing the request information:
request = %Tornex.Query{
resource: "user",
resource_id: 2383326,
key: api_key,
selections: ["attacks", "basic"],
key_owner: 2383326,
nice: 10
}
The query struct must contain the following values:
resource
: The name of the resource.resource_id
: The ID of the resource even if the request is against the key owner's user, faction, etc.key
: The API key to be used.key_owner
: Any other unique identifier (suggested to use Torn ID) of the API key owner.nice
: The priority of the request (following the Linux niceness values) where -20 is the highest priority and 19 is the lowest priority.
APIv2
To use the auto-generated client for APIv2, you will need to add the Tornium/torngen_elixir_client library to your dependencies. Without installing this client, you will have to use the APIv1 style requests through Tornex.Query
and prepend v2
to the resource (e.g. v2/user
).
Create a Tornex.SpecQuery
struct containing the request information:
request =
Tornex.SpecQuery.new()
|> Tornex.SpecQuery.put_path(Torngen.Client.Path.User.Attacks)
|> Tornex.SpecQuery.put_path(Torngen.Client.Path.User.Basic)
|> Tornex.SpecQuery.put_key(api_key)
Once the query struct has been constructed, the request can be enqueued into the key owner's queue with Tornex.Scheduler.Bucket.enqueue/1
to be made depending on the request's priority and the state of the key owner's queue. Alternatively, the request can be performed immediately with Tornex.API.get/1
which performs no ratelimiting. However, both methods are blocking.
response = Tornex.Scheduler.Bucket.enqueue(request)
case response do
{:error, :timeout} ->
IO.puts("timeout")
{:error, _} ->
IO.puts("unknown")
%{"name" => name, "attacks" => attacks} ->
IO.puts("#{name} => #{Enum.count(attacks)} attacks")
end
Documentation
Documentation can be generated with ExDoc and can be found at https://hexdocs.pm/tornex.
License
Copyright 2024-2025 tiksan
This project is licensed under Apache 2.0; see LICENSE.md for more details.