initial commit
This commit is contained in:
commit
56de31d5c5
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
zig-cache
|
||||
zig-out
|
35
build.zig
Normal file
35
build.zig
Normal file
@ -0,0 +1,35 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "artificer",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const exe_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_exe_unit_tests.step);
|
||||
}
|
7
build.zig.zon
Normal file
7
build.zig.zon
Normal file
@ -0,0 +1,7 @@
|
||||
.{
|
||||
.name = "artificer",
|
||||
.version = "0.1.0",
|
||||
.minimum_zig_version = "0.12.0",
|
||||
.dependencies = .{ },
|
||||
.paths = .{ "" },
|
||||
}
|
544
src/artifacts.zig
Normal file
544
src/artifacts.zig
Normal file
@ -0,0 +1,544 @@
|
||||
const std = @import("std");
|
||||
const json = std.json;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// Specification: https://api.artifactsmmo.com/docs
|
||||
|
||||
// TODO: Convert 'expiration' date time strings into date time objects
|
||||
|
||||
const ArtifactsAPI = @This();
|
||||
|
||||
pub const APIErrors = error {
|
||||
RequestFailed,
|
||||
ParseFailed
|
||||
};
|
||||
|
||||
const ServerStatus = struct {
|
||||
// TODO: Parse the rest of the fields
|
||||
allocator: Allocator,
|
||||
status: []const u8,
|
||||
version: []const u8,
|
||||
|
||||
fn parse(allocator: Allocator, object: json.ObjectMap) !ServerStatus {
|
||||
const status = getJsonString(object, "status") orelse return error.MissingStatus;
|
||||
const version = getJsonString(object, "version") orelse return error.MissingVersion;
|
||||
|
||||
return ServerStatus{
|
||||
.allocator = allocator,
|
||||
.status = try allocator.dupe(u8, status),
|
||||
.version = try allocator.dupe(u8, version)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: ServerStatus) void {
|
||||
self.allocator.free(self.status);
|
||||
self.allocator.free(self.version);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Character = struct {
|
||||
pub const SkillStats = struct {
|
||||
level: i64,
|
||||
xp: i64,
|
||||
max_xp: i64,
|
||||
|
||||
fn parse(object: json.ObjectMap, level: []const u8, xp: []const u8, max_xp: []const u8) !SkillStats {
|
||||
return SkillStats{
|
||||
.level = getJsonInteger(object, level) orelse return error.MissingProperty,
|
||||
.xp = getJsonInteger(object, xp) orelse return error.MissingProperty,
|
||||
.max_xp = getJsonInteger(object, max_xp) orelse return error.MissingProperty,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const CombatStats = struct {
|
||||
attack: i64,
|
||||
damage: i64,
|
||||
resistance: i64,
|
||||
|
||||
fn parse(object: json.ObjectMap, attack: []const u8, damage: []const u8, resistance: []const u8) !CombatStats {
|
||||
return CombatStats{
|
||||
.attack = getJsonInteger(object, attack) orelse return error.MissingProperty,
|
||||
.damage = getJsonInteger(object, damage) orelse return error.MissingProperty,
|
||||
.resistance = getJsonInteger(object, resistance) orelse return error.MissingProperty,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Equipment = struct {
|
||||
pub const Consumable = struct {
|
||||
name: []u8,
|
||||
quantity: i64,
|
||||
|
||||
fn parse(allocator: Allocator, obj: json.ObjectMap, name: []const u8, quantity: []const u8) !Consumable {
|
||||
return Consumable{
|
||||
.name = (try dupeJsonString(allocator, obj, name)) orelse return error.MissingProperty,
|
||||
.quantity = getJsonInteger(obj, quantity) orelse return error.MissingProperty,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
weapon: []u8,
|
||||
shield: []u8,
|
||||
helmet: []u8,
|
||||
body_armor: []u8,
|
||||
leg_armor: []u8,
|
||||
boots: []u8,
|
||||
|
||||
ring1: []u8,
|
||||
ring2: []u8,
|
||||
amulet: []u8,
|
||||
|
||||
artifact1: []u8,
|
||||
artifact2: []u8,
|
||||
artifact3: []u8,
|
||||
|
||||
consumable1: Consumable,
|
||||
consumable2: Consumable,
|
||||
|
||||
fn parse(allocator: Allocator, obj: json.ObjectMap) !Equipment {
|
||||
return Equipment{
|
||||
.weapon = (try dupeJsonString(allocator, obj, "weapon_slot")) orelse return error.MissingProperty,
|
||||
.shield = (try dupeJsonString(allocator, obj, "shield_slot")) orelse return error.MissingProperty,
|
||||
.helmet = (try dupeJsonString(allocator, obj, "helmet_slot")) orelse return error.MissingProperty,
|
||||
.body_armor = (try dupeJsonString(allocator, obj, "body_armor_slot")) orelse return error.MissingProperty,
|
||||
.leg_armor = (try dupeJsonString(allocator, obj, "leg_armor_slot")) orelse return error.MissingProperty,
|
||||
.boots = (try dupeJsonString(allocator, obj, "boots_slot")) orelse return error.MissingProperty,
|
||||
.ring1 = (try dupeJsonString(allocator, obj, "ring1_slot")) orelse return error.MissingProperty,
|
||||
.ring2 = (try dupeJsonString(allocator, obj, "ring2_slot")) orelse return error.MissingProperty,
|
||||
.amulet = (try dupeJsonString(allocator, obj, "amulet_slot")) orelse return error.MissingProperty,
|
||||
.artifact1 = (try dupeJsonString(allocator, obj, "artifact1_slot")) orelse return error.MissingProperty,
|
||||
.artifact2 = (try dupeJsonString(allocator, obj, "artifact2_slot")) orelse return error.MissingProperty,
|
||||
.artifact3 = (try dupeJsonString(allocator, obj, "artifact3_slot")) orelse return error.MissingProperty,
|
||||
.consumable1 = try Consumable.parse(allocator, obj, "consumable1_slot", "consumable1_slot_quantity"),
|
||||
.consumable2 = try Consumable.parse(allocator, obj, "consumable2_slot", "consumable2_slot_quantity"),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
|
||||
name: []u8,
|
||||
skin: []u8,
|
||||
account: ?[]u8,
|
||||
total_xp: i64,
|
||||
gold: i64,
|
||||
hp: i64,
|
||||
haste: i64,
|
||||
x: i64,
|
||||
y: i64,
|
||||
cooldown: i64,
|
||||
cooldown_expiration: []u8,
|
||||
|
||||
combat: SkillStats,
|
||||
mining: SkillStats,
|
||||
woodcutting: SkillStats,
|
||||
fishing: SkillStats,
|
||||
weaponcrafting: SkillStats,
|
||||
gearcrafting: SkillStats,
|
||||
jewelrycrafting: SkillStats,
|
||||
cooking: SkillStats,
|
||||
|
||||
water: CombatStats,
|
||||
fire: CombatStats,
|
||||
earth: CombatStats,
|
||||
air: CombatStats,
|
||||
|
||||
equipment: Equipment,
|
||||
|
||||
fn parse(child_allocator: Allocator, obj: json.ObjectMap) !Character {
|
||||
var arena = try child_allocator.create(std.heap.ArenaAllocator);
|
||||
errdefer child_allocator.destroy(arena);
|
||||
|
||||
arena.* = std.heap.ArenaAllocator.init(child_allocator);
|
||||
errdefer arena.deinit();
|
||||
|
||||
const allocator = arena.allocator();
|
||||
|
||||
return Character{
|
||||
.arena = arena,
|
||||
.account = try dupeJsonString(allocator, obj, "account"),
|
||||
.name = (try dupeJsonString(allocator, obj, "name")) orelse return error.MissingProperty,
|
||||
.skin = (try dupeJsonString(allocator, obj, "skin")) orelse return error.MissingProperty,
|
||||
|
||||
.total_xp = getJsonInteger(obj, "total_xp") orelse return error.MissingProperty,
|
||||
.gold = getJsonInteger(obj, "gold") orelse return error.MissingProperty,
|
||||
.hp = getJsonInteger(obj, "hp") orelse return error.MissingProperty,
|
||||
.haste = getJsonInteger(obj, "haste") orelse return error.MissingProperty,
|
||||
.x = getJsonInteger(obj, "x") orelse return error.MissingProperty,
|
||||
.y = getJsonInteger(obj, "y") orelse return error.MissingProperty,
|
||||
.cooldown = getJsonInteger(obj, "cooldown") orelse return error.MissingProperty,
|
||||
.cooldown_expiration = (try dupeJsonString(allocator, obj, "cooldown_expiration")) orelse return error.MissingProperty,
|
||||
|
||||
.combat = try SkillStats.parse(obj, "level", "xp", "max_xp"),
|
||||
.mining = try SkillStats.parse(obj, "mining_level", "mining_xp", "mining_max_xp"),
|
||||
.woodcutting = try SkillStats.parse(obj, "woodcutting_level", "woodcutting_xp", "woodcutting_max_xp"),
|
||||
.fishing = try SkillStats.parse(obj, "fishing_level", "fishing_xp", "fishing_max_xp"),
|
||||
.weaponcrafting = try SkillStats.parse(obj, "weaponcrafting_level", "weaponcrafting_xp", "weaponcrafting_max_xp"),
|
||||
.gearcrafting = try SkillStats.parse(obj, "gearcrafting_level", "gearcrafting_xp", "gearcrafting_max_xp"),
|
||||
.jewelrycrafting = try SkillStats.parse(obj, "jewelrycrafting_level", "jewelrycrafting_xp", "jewelrycrafting_max_xp"),
|
||||
.cooking = try SkillStats.parse(obj, "cooking_level", "cooking_xp", "cooking_max_xp"),
|
||||
|
||||
.water = try CombatStats.parse(obj, "attack_water", "dmg_water", "res_water"),
|
||||
.fire = try CombatStats.parse(obj, "attack_fire", "dmg_fire", "res_fire"),
|
||||
.earth = try CombatStats.parse(obj, "attack_earth", "dmg_earth", "res_earth"),
|
||||
.air = try CombatStats.parse(obj, "attack_air", "dmg_air", "res_air"),
|
||||
|
||||
.equipment = try Equipment.parse(allocator, obj)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Character) void {
|
||||
var child_allocator = self.arena.child_allocator;
|
||||
self.arena.deinit();
|
||||
child_allocator.destroy(self.arena);
|
||||
}
|
||||
};
|
||||
|
||||
pub const CharacterList = struct {
|
||||
allocator: Allocator,
|
||||
items: []Character,
|
||||
|
||||
pub fn deinit(self: CharacterList) void {
|
||||
for (self.items) |*char| {
|
||||
char.deinit();
|
||||
}
|
||||
self.allocator.free(self.items);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Cooldown = struct {
|
||||
pub const Reason = enum {
|
||||
movement,
|
||||
fight,
|
||||
crafting,
|
||||
gathering,
|
||||
buy_ge,
|
||||
sell_ge,
|
||||
delete_item,
|
||||
deposit_bank,
|
||||
withdraw_bank,
|
||||
equip,
|
||||
unequip,
|
||||
task,
|
||||
recycling,
|
||||
|
||||
fn parse(str: []const u8) ?Reason {
|
||||
const eql = std.mem.eql;
|
||||
if (eql(u8, str, "movement")) {
|
||||
return .movement;
|
||||
} else if (eql(u8, str, "fight")) {
|
||||
return .fight;
|
||||
} else if (eql(u8, str, "crafting")) {
|
||||
return .crafting;
|
||||
} else if (eql(u8, str, "gathering")) {
|
||||
return .gathering;
|
||||
} else if (eql(u8, str, "buy_ge")) {
|
||||
return .buy_ge;
|
||||
} else if (eql(u8, str, "sell_ge")) {
|
||||
return .sell_ge;
|
||||
} else if (eql(u8, str, "delete_item")) {
|
||||
return .delete_item;
|
||||
} else if (eql(u8, str, "deposit_bank")) {
|
||||
return .deposit_bank;
|
||||
} else if (eql(u8, str, "withdraw_bank")) {
|
||||
return .withdraw_bank;
|
||||
} else if (eql(u8, str, "equip")) {
|
||||
return .equip;
|
||||
} else if (eql(u8, str, "unequip")) {
|
||||
return .unequip;
|
||||
} else if (eql(u8, str, "task")) {
|
||||
return .task;
|
||||
} else if (eql(u8, str, "recycling")) {
|
||||
return .recycling;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
total_seconds: i64,
|
||||
remaining_seconds: i64,
|
||||
expiration: []u8,
|
||||
reason: Reason,
|
||||
|
||||
fn parse(allocator: Allocator, obj: json.ObjectMap) !Cooldown {
|
||||
const reason = getJsonString(obj, "reason") orelse return error.MissingProperty;
|
||||
return Cooldown{
|
||||
.allocator = allocator,
|
||||
.total_seconds = getJsonInteger(obj, "totalSeconds") orelse return error.MissingProperty,
|
||||
.remaining_seconds = getJsonInteger(obj, "remainingSeconds") orelse return error.MissingProperty,
|
||||
.expiration = (try dupeJsonString(allocator, obj, "expiration")) orelse return error.MissingProperty,
|
||||
.reason = Reason.parse(reason) orelse return error.UnknownReason
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Cooldown) void {
|
||||
self.allocator.free(self.expiration);
|
||||
}
|
||||
};
|
||||
|
||||
pub const FightResult = struct {
|
||||
cooldown: Cooldown,
|
||||
|
||||
fn parse(allocator: Allocator, obj: json.ObjectMap) !FightResult {
|
||||
const cooldown = getJsonObject(obj, "cooldown") orelse return error.MissingProperty;
|
||||
|
||||
return FightResult{
|
||||
.cooldown = try Cooldown.parse(allocator, cooldown)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: FightResult) void {
|
||||
self.cooldown.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
const ArtifactsFetchResult = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
status: std.http.Status,
|
||||
body: ?json.Value = null,
|
||||
|
||||
fn deinit(self: ArtifactsFetchResult) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
client: std.http.Client,
|
||||
|
||||
server: []u8,
|
||||
server_uri: std.Uri,
|
||||
|
||||
token: ?[]u8 = null,
|
||||
|
||||
pub fn init(allocator: Allocator) !ArtifactsAPI {
|
||||
const server = try allocator.dupe(u8, "https://api.artifactsmmo.com");
|
||||
const server_uri = std.Uri.parse(server) catch unreachable;
|
||||
|
||||
return ArtifactsAPI{
|
||||
.allocator = allocator,
|
||||
.client = .{ .allocator = allocator },
|
||||
.server = server,
|
||||
.server_uri = server_uri
|
||||
};
|
||||
}
|
||||
|
||||
fn fetch(self: *ArtifactsAPI, method: std.http.Method, path: []const u8) !ArtifactsFetchResult {
|
||||
var uri = self.server_uri;
|
||||
uri.path = .{ .raw = path };
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||
errdefer arena.deinit();
|
||||
|
||||
var response_storage = std.ArrayList(u8).init(arena.allocator());
|
||||
|
||||
var opts = std.http.Client.FetchOptions{
|
||||
.method = method,
|
||||
.location = .{ .uri = uri },
|
||||
.response_storage = .{ .dynamic = &response_storage }
|
||||
};
|
||||
|
||||
var authorization_header: ?[]u8 = null;
|
||||
defer if (authorization_header) |str| self.allocator.free(str);
|
||||
|
||||
if (self.token) |token| {
|
||||
authorization_header = try std.fmt.allocPrint(self.allocator, "Bearer {s}", .{token});
|
||||
opts.headers.authorization = .{ .override = authorization_header.? };
|
||||
}
|
||||
|
||||
const result = try self.client.fetch(opts);
|
||||
|
||||
if (result.status != .ok) {
|
||||
return ArtifactsFetchResult{
|
||||
.arena = arena,
|
||||
.status = result.status
|
||||
};
|
||||
}
|
||||
|
||||
const response_body = response_storage.items;
|
||||
|
||||
const parsed = try json.parseFromSliceLeaky(json.Value, arena.allocator(), response_body, .{ .allocate = .alloc_if_needed });
|
||||
if (parsed != json.Value.object) {
|
||||
return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
return ArtifactsFetchResult{
|
||||
.status = result.status,
|
||||
.arena = arena,
|
||||
.body = parsed.object.get("data")
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setServer(self: *ArtifactsAPI, url: []const u8) !void {
|
||||
const url_dupe = self.allocator.dupe(u8, url);
|
||||
errdefer self.allocator.free(url_dupe);
|
||||
|
||||
const uri = try std.Uri.parse(url_dupe);
|
||||
|
||||
self.allocator.free(self.server);
|
||||
self.server = url_dupe;
|
||||
self.server_uri = uri;
|
||||
}
|
||||
|
||||
pub fn setToken(self: *ArtifactsAPI, token: ?[]const u8) !void {
|
||||
var new_token: ?[]u8 = null;
|
||||
if (token != null) {
|
||||
new_token = try self.allocator.dupe(u8, token.?);
|
||||
}
|
||||
|
||||
if (self.token) |str| self.allocator.free(str);
|
||||
self.token = new_token;
|
||||
}
|
||||
|
||||
fn getJsonString(object: json.ObjectMap, name: []const u8) ?[]const u8 {
|
||||
const value = object.get(name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.? != json.Value.string) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.?.string;
|
||||
}
|
||||
|
||||
fn dupeJsonString(allocator: Allocator, object: json.ObjectMap, name: []const u8) !?[]u8 {
|
||||
const str = getJsonString(object, name) orelse return null;
|
||||
return try allocator.dupe(u8, str);
|
||||
}
|
||||
|
||||
fn getJsonInteger(object: json.ObjectMap, name: []const u8) ?i64 {
|
||||
const value = object.get(name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value.? != json.Value.integer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.?.integer;
|
||||
}
|
||||
|
||||
fn asJsonObject(value: json.Value) ?json.ObjectMap {
|
||||
if (value != json.Value.object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.object;
|
||||
}
|
||||
|
||||
fn asJsonArray(value: json.Value) ?json.Array {
|
||||
if (value != json.Value.array) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.array;
|
||||
}
|
||||
|
||||
fn getJsonObject(object: json.ObjectMap, name: []const u8) ?json.ObjectMap {
|
||||
const value = object.get(name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return asJsonObject(value.?);
|
||||
}
|
||||
|
||||
pub fn getServerStatus(self: *ArtifactsAPI) !ServerStatus {
|
||||
const result = try self.fetch(.GET, "/");
|
||||
defer result.deinit();
|
||||
|
||||
if (result.status != .ok) {
|
||||
return APIErrors.RequestFailed;
|
||||
}
|
||||
if (result.body == null) {
|
||||
return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
const body = asJsonObject(result.body.?) orelse return APIErrors.ParseFailed;
|
||||
return ServerStatus.parse(self.allocator, body) catch return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
pub fn getCharacter(self: *ArtifactsAPI, name: []const u8) !Character {
|
||||
const path = try std.fmt.allocPrint(self.allocator, "/characters/{s}", .{name});
|
||||
defer self.allocator.free(path);
|
||||
|
||||
const result = try self.fetch(.GET, path);
|
||||
defer result.deinit();
|
||||
|
||||
if (result.status != .ok) {
|
||||
return APIErrors.RequestFailed;
|
||||
}
|
||||
if (result.body == null) {
|
||||
return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
const body = asJsonObject(result.body.?) orelse return APIErrors.ParseFailed;
|
||||
return Character.parse(self.allocator, body) catch return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
pub fn listMyCharacters(self: *ArtifactsAPI) !CharacterList {
|
||||
const path = try std.fmt.allocPrint(self.allocator, "/my/characters", .{});
|
||||
defer self.allocator.free(path);
|
||||
|
||||
const result = try self.fetch(.GET, path);
|
||||
defer result.deinit();
|
||||
|
||||
if (result.status != .ok) {
|
||||
return APIErrors.RequestFailed;
|
||||
}
|
||||
if (result.body == null) {
|
||||
return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
const body = asJsonArray(result.body.?) orelse return APIErrors.ParseFailed;
|
||||
|
||||
var characters = try std.ArrayList(Character).initCapacity(self.allocator, body.items.len);
|
||||
errdefer {
|
||||
for (characters.items) |*char| {
|
||||
char.deinit();
|
||||
}
|
||||
characters.deinit();
|
||||
}
|
||||
|
||||
for (body.items) |character_json| {
|
||||
const character_obj = asJsonObject(character_json) orelse return APIErrors.ParseFailed;
|
||||
const char = Character.parse(self.allocator, character_obj) catch return APIErrors.ParseFailed;
|
||||
|
||||
characters.appendAssumeCapacity(char);
|
||||
}
|
||||
|
||||
|
||||
return CharacterList{
|
||||
.allocator = self.allocator,
|
||||
.items = characters.items
|
||||
};
|
||||
}
|
||||
|
||||
pub fn actionFight(self: *ArtifactsAPI, name: []const u8) !FightResult {
|
||||
const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/fight", .{name});
|
||||
defer self.allocator.free(path);
|
||||
|
||||
const result = try self.fetch(.POST, path);
|
||||
defer result.deinit();
|
||||
|
||||
if (result.status != .ok) {
|
||||
return APIErrors.RequestFailed;
|
||||
}
|
||||
if (result.body == null) {
|
||||
return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
const body = asJsonObject(result.body.?) orelse return APIErrors.ParseFailed;
|
||||
return FightResult.parse(self.allocator, body) catch return APIErrors.ParseFailed;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ArtifactsAPI) void {
|
||||
self.client.deinit();
|
||||
self.allocator.free(self.server);
|
||||
if (self.token) |str| self.allocator.free(str);
|
||||
}
|
26
src/main.zig
Normal file
26
src/main.zig
Normal file
@ -0,0 +1,26 @@
|
||||
const std = @import("std");
|
||||
const ArtifactsAPI = @import("artifacts.zig");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var api = try ArtifactsAPI.init(allocator);
|
||||
defer api.deinit();
|
||||
|
||||
{ // Set auth token from environment variable
|
||||
var env = try std.process.getEnvMap(allocator);
|
||||
defer env.deinit();
|
||||
|
||||
const token = env.get("ARTIFACTS_TOKEN");
|
||||
try api.setToken(token);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const result = try api.actionFight("Daisy");
|
||||
defer result.deinit();
|
||||
|
||||
std.time.sleep(std.time.ns_per_s * (@as(u64, @intCast(result.cooldown.remaining_seconds)) + 1));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user