mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-09 17:25:38 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f00a63c2 | ||
|
|
05f427bcd7 | ||
|
|
69f4c41534 | ||
|
|
30b9239a8b | ||
|
|
2061a83c59 | ||
|
|
24e723de07 | ||
|
|
27b5694885 | ||
|
|
4093f28cee | ||
|
|
08aaf2f2dd | ||
|
|
5a927e5ba5 | ||
|
|
10fafcf59f | ||
|
|
be87591801 | ||
|
|
086d4378d3 | ||
|
|
e982275905 | ||
|
|
77c02703e9 | ||
|
|
0ef27d4f95 | ||
|
|
5edc27744e | ||
|
|
02ff887fee | ||
|
|
3a30eeb59f | ||
|
|
79af8fb601 | ||
|
|
f9f00faa0e | ||
|
|
a3c41e84e4 | ||
|
|
7f21f33351 | ||
|
|
568f682cee | ||
|
|
901c4c8ca4 | ||
|
|
3dbba97f9c | ||
|
|
3475620267 | ||
|
|
8936a5e5d8 | ||
|
|
719e34f9bc | ||
|
|
df955ff8b0 | ||
|
|
4dc74022b4 | ||
|
|
c2b7d07208 | ||
|
|
21e76a6f05 | ||
|
|
89c0d6fad6 | ||
|
|
b74d15b1ee | ||
|
|
10313438bf | ||
|
|
a764217948 | ||
|
|
d81d6fb937 | ||
|
|
4c0f7ab7f9 | ||
|
|
1c48945a96 | ||
|
|
850901f62f | ||
|
|
4822854e30 | ||
|
|
f580538331 | ||
|
|
0d70c555e6 | ||
|
|
c5f6cf0080 | ||
|
|
6ff7b3bc9a | ||
|
|
346c2c65b0 | ||
|
|
a6445fd500 | ||
|
|
358e43e508 | ||
|
|
20c5ba6b63 | ||
|
|
661658a6e8 | ||
|
|
0a6f224ed3 | ||
|
|
e7bb29693f | ||
|
|
32dfd50461 | ||
|
|
bfec385dce | ||
|
|
04278f99d7 | ||
|
|
9d3db19dc1 | ||
|
|
3953e33f37 | ||
|
|
611fdd56d0 | ||
|
|
36a4fd0f35 | ||
|
|
488984a988 | ||
|
|
1b183d6e58 | ||
|
|
3dcb6d30b5 | ||
|
|
1d11788f89 | ||
|
|
d3822128ab | ||
|
|
6ac7836505 |
162
CHANGELOG.md
162
CHANGELOG.md
@@ -2,6 +2,168 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.66.24](https://github.com/wanderer-industries/wanderer/compare/v1.66.23...v1.66.24) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.23](https://github.com/wanderer-industries/wanderer/compare/v1.66.22...v1.66.23) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.22](https://github.com/wanderer-industries/wanderer/compare/v1.66.21...v1.66.22) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.21](https://github.com/wanderer-industries/wanderer/compare/v1.66.20...v1.66.21) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed kills fetching based on env settings
|
||||
|
||||
## [v1.66.20](https://github.com/wanderer-industries/wanderer/compare/v1.66.19...v1.66.20) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.19](https://github.com/wanderer-industries/wanderer/compare/v1.66.18...v1.66.19) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Added check for offline characters timeouts
|
||||
|
||||
## [v1.66.18](https://github.com/wanderer-industries/wanderer/compare/v1.66.17...v1.66.18) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.17](https://github.com/wanderer-industries/wanderer/compare/v1.66.16...v1.66.17) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Increased tracking pause timeout for offline characters up to 10 hours
|
||||
|
||||
## [v1.66.16](https://github.com/wanderer-industries/wanderer/compare/v1.66.15...v1.66.16) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Increased tracking pause timeout for offline characters up to 10 hours
|
||||
|
||||
## [v1.66.15](https://github.com/wanderer-industries/wanderer/compare/v1.66.14...v1.66.15) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Added back arm docker image build
|
||||
|
||||
## [v1.66.14](https://github.com/wanderer-industries/wanderer/compare/v1.66.13...v1.66.14) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: fixed online updates
|
||||
|
||||
## [v1.66.13](https://github.com/wanderer-industries/wanderer/compare/v1.66.12...v1.66.13) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: fixed location tracking issues
|
||||
|
||||
## [v1.66.12](https://github.com/wanderer-industries/wanderer/compare/v1.66.11...v1.66.12) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.11](https://github.com/wanderer-industries/wanderer/compare/v1.66.10...v1.66.11) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: fixed refresh character tokens
|
||||
|
||||
## [v1.66.10](https://github.com/wanderer-industries/wanderer/compare/v1.66.9...v1.66.10) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.9](https://github.com/wanderer-industries/wanderer/compare/v1.66.8...v1.66.9) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.8](https://github.com/wanderer-industries/wanderer/compare/v1.66.7...v1.66.8) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* fixed disable detailed kills env check
|
||||
|
||||
## [v1.66.7](https://github.com/wanderer-industries/wanderer/compare/v1.66.6...v1.66.7) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* fixed disable detailed kills env check
|
||||
|
||||
## [v1.66.6](https://github.com/wanderer-industries/wanderer/compare/v1.66.5...v1.66.6) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* respect error limits for ESI APIs
|
||||
|
||||
## [v1.66.5](https://github.com/wanderer-industries/wanderer/compare/v1.66.4...v1.66.5) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* respect error limits for ESI APIs
|
||||
|
||||
## [v1.66.4](https://github.com/wanderer-industries/wanderer/compare/v1.66.3...v1.66.4) (2025-06-06)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* respect error limits for ESI APIs
|
||||
|
||||
## [v1.66.3](https://github.com/wanderer-industries/wanderer/compare/v1.66.2...v1.66.3) (2025-06-05)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.2](https://github.com/wanderer-industries/wanderer/compare/v1.66.1...v1.66.2) (2025-06-05)
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { useCallback } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useAutoAnimate } from '@formkit/auto-animate/react';
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
|
||||
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import classes from './Characters.module.scss';
|
||||
import { isDocked } from '@/hooks/Mapper/helpers/isDocked.ts';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { CharacterTypeRaw } from '@/hooks/Mapper/types';
|
||||
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useAutoAnimate } from '@formkit/auto-animate/react';
|
||||
import clsx from 'clsx';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import classes from './Characters.module.scss';
|
||||
interface CharactersProps {
|
||||
data: CharacterTypeRaw[];
|
||||
}
|
||||
@@ -17,13 +16,22 @@ export const Characters = ({ data }: CharactersProps) => {
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
const {
|
||||
outCommand,
|
||||
data: { mainCharacterEveId, followingCharacterEveId },
|
||||
} = useMapRootState();
|
||||
|
||||
const handleSelect = useCallback((character: CharacterTypeRaw) => {
|
||||
const handleSelect = useCallback(async (character: CharacterTypeRaw) => {
|
||||
if (!character) {
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.startTracking,
|
||||
data: { character_eve_id: character.eve_id },
|
||||
});
|
||||
emitMapEvent({
|
||||
name: Commands.centerSystem,
|
||||
data: character?.location?.solar_system_id?.toString(),
|
||||
data: character.location?.solar_system_id?.toString(),
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -37,14 +45,26 @@ export const Characters = ({ data }: CharactersProps) => {
|
||||
className={clsx(
|
||||
'overflow-hidden relative',
|
||||
'flex w-[35px] h-[35px] rounded-[4px] border-[1px] border-solid bg-transparent cursor-pointer',
|
||||
'transition-colors duration-250',
|
||||
'transition-colors duration-250 hover:bg-stone-300/90',
|
||||
{
|
||||
['border-stone-800/90']: !character.online,
|
||||
['border-lime-600/70']: character.online,
|
||||
},
|
||||
)}
|
||||
title={character.name}
|
||||
title={character.tracking_paused ? `${character.name} - Tracking Paused (click to resume)` : character.name}
|
||||
>
|
||||
{character.tracking_paused && (
|
||||
<>
|
||||
<span
|
||||
className={clsx(
|
||||
'absolute flex flex-col p-[2px] top-[0px] left-[0px] w-[35px] h-[35px]',
|
||||
'text-yellow-500 text-[9px] z-10 bg-gray-800/40',
|
||||
'pi',
|
||||
PrimeIcons.PAUSE,
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{mainCharacterEveId === character.eve_id && (
|
||||
<span
|
||||
className={clsx(
|
||||
@@ -55,6 +75,7 @@ export const Characters = ({ data }: CharactersProps) => {
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{followingCharacterEveId === character.eve_id && (
|
||||
<span
|
||||
className={clsx(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import {
|
||||
CommandCharacterAdded,
|
||||
CommandCharacterRemoved,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
CommandCharacterUpdated,
|
||||
CommandPresentCharacters,
|
||||
} from '@/hooks/Mapper/types';
|
||||
import { useCallback, useRef } from 'react';
|
||||
|
||||
export const useCommandsCharacters = () => {
|
||||
const { update } = useMapState();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandInit } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { convertConnection2Edge, convertSystem2Node } from '../../helpers';
|
||||
import { MapData, useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { CommandInit } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import { convertConnection2Edge, convertSystem2Node } from '../../helpers';
|
||||
|
||||
export const useMapInit = () => {
|
||||
const rf = useReactFlow();
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Characters } from '../characters/Characters';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { sortOnlineFunc } from '@/hooks/Mapper/components/hooks/useGetOwnOnlineCharacters.ts';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { WithChildren } from '@/hooks/Mapper/types/common.ts';
|
||||
import { Button } from 'primereact/button';
|
||||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
import { Characters } from '../characters/Characters';
|
||||
|
||||
const Topbar = ({ children }: WithChildren) => {
|
||||
const {
|
||||
data: { characters, userCharacters, pings },
|
||||
data: { characters, userCharacters },
|
||||
} = useMapRootState();
|
||||
|
||||
const charsToShow = useMemo(() => {
|
||||
|
||||
@@ -33,6 +33,7 @@ export type CharacterTypeRaw = {
|
||||
corporation_id: number;
|
||||
corporation_name: string;
|
||||
corporation_ticker: string;
|
||||
tracking_paused: boolean;
|
||||
};
|
||||
|
||||
export interface TrackingCharacter {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SolarSystemRawType, SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types/system.ts';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types/connection.ts';
|
||||
import { WormholeDataRaw } from '@/hooks/Mapper/types/wormholes.ts';
|
||||
import { ActivitySummary, CharacterTypeRaw, TrackingCharacter } from '@/hooks/Mapper/types/character.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { DetailedKill, Kill } from '@/hooks/Mapper/types/kills.ts';
|
||||
import { CommentType, PingData, SystemSignature, UserPermissions } from '@/hooks/Mapper/types';
|
||||
import { ActivitySummary, CharacterTypeRaw, TrackingCharacter } from '@/hooks/Mapper/types/character.ts';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types/connection.ts';
|
||||
import { DetailedKill, Kill } from '@/hooks/Mapper/types/kills.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { SolarSystemRawType, SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types/system.ts';
|
||||
import { WormholeDataRaw } from '@/hooks/Mapper/types/wormholes.ts';
|
||||
|
||||
export enum Commands {
|
||||
init = 'init',
|
||||
@@ -260,6 +260,7 @@ export enum OutCommand {
|
||||
updateMainCharacter = 'updateMainCharacter',
|
||||
addPing = 'add_ping',
|
||||
cancelPing = 'cancel_ping',
|
||||
startTracking = 'startTracking',
|
||||
|
||||
// Only UI commands
|
||||
openSettings = 'open_settings',
|
||||
|
||||
@@ -53,16 +53,6 @@ public_api_disabled =
|
||||
|> get_var_from_path_or_env("WANDERER_PUBLIC_API_DISABLED", "false")
|
||||
|> String.to_existing_atom()
|
||||
|
||||
character_api_disabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_CHARACTER_API_DISABLED", "true")
|
||||
|> String.to_existing_atom()
|
||||
|
||||
zkill_preload_disabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_ZKILL_PRELOAD_DISABLED", "false")
|
||||
|> String.to_existing_atom()
|
||||
|
||||
map_subscriptions_enabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_MAP_SUBSCRIPTIONS_ENABLED", "false")
|
||||
@@ -128,8 +118,10 @@ config :wanderer_app,
|
||||
corp_id: System.get_env("WANDERER_CORP_ID", "-1") |> String.to_integer(),
|
||||
corp_wallet: System.get_env("WANDERER_CORP_WALLET", ""),
|
||||
public_api_disabled: public_api_disabled,
|
||||
character_api_disabled: character_api_disabled,
|
||||
zkill_preload_disabled: zkill_preload_disabled,
|
||||
character_api_disabled:
|
||||
System.get_env("WANDERER_CHARACTER_API_DISABLED", "true") |> String.to_existing_atom(),
|
||||
zkill_preload_disabled:
|
||||
System.get_env("WANDERER_ZKILL_PRELOAD_DISABLED", "false") |> String.to_existing_atom(),
|
||||
map_subscriptions_enabled: map_subscriptions_enabled,
|
||||
map_connection_auto_expire_hours: map_connection_auto_expire_hours,
|
||||
map_connection_auto_eol_hours: map_connection_auto_eol_hours,
|
||||
|
||||
@@ -191,7 +191,7 @@ defmodule WandererApp.Character do
|
||||
{:ok, result} ->
|
||||
{:ok, result |> prepare_search_results()}
|
||||
|
||||
{:error, error} ->
|
||||
error ->
|
||||
Logger.warning("#{__MODULE__} failed search: #{inspect(error)}")
|
||||
{:ok, []}
|
||||
end
|
||||
@@ -251,13 +251,22 @@ defmodule WandererApp.Character do
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_merge_map_character_settings(character, map_id, true), do: character
|
||||
defp maybe_merge_map_character_settings(%{id: character_id} = character, map_id, true) do
|
||||
{:ok, tracking_paused} =
|
||||
WandererApp.Cache.lookup("character:#{character_id}:tracking_paused", false)
|
||||
|
||||
character
|
||||
|> Map.merge(%{tracking_paused: tracking_paused})
|
||||
end
|
||||
|
||||
defp maybe_merge_map_character_settings(
|
||||
%{id: character_id} = character,
|
||||
map_id,
|
||||
_character_is_present
|
||||
) do
|
||||
{:ok, tracking_paused} =
|
||||
WandererApp.Cache.lookup("character:#{character_id}:tracking_paused", false)
|
||||
|
||||
WandererApp.MapCharacterSettingsRepo.get(map_id, character_id)
|
||||
|> case do
|
||||
{:ok, settings} when not is_nil(settings) ->
|
||||
@@ -270,6 +279,7 @@ defmodule WandererApp.Character do
|
||||
|> Map.put(:online, false)
|
||||
|> Map.merge(@default_character_tracking_data)
|
||||
end
|
||||
|> Map.merge(%{tracking_paused: tracking_paused})
|
||||
end
|
||||
|
||||
defp prepare_search_results(result) do
|
||||
|
||||
@@ -33,8 +33,16 @@ defmodule WandererApp.Character.Tracker do
|
||||
status: binary()
|
||||
}
|
||||
|
||||
@online_error_timeout :timer.minutes(3)
|
||||
@forbidden_ttl :timer.minutes(1)
|
||||
@pause_tracking_timeout :timer.minutes(60 * 10)
|
||||
@offline_timeout :timer.minutes(5)
|
||||
@online_error_timeout :timer.minutes(2)
|
||||
@ship_error_timeout :timer.minutes(2)
|
||||
@location_error_timeout :timer.minutes(2)
|
||||
@online_forbidden_ttl :timer.seconds(7)
|
||||
@online_limit_ttl :timer.seconds(7)
|
||||
@forbidden_ttl :timer.seconds(5)
|
||||
@limit_ttl :timer.seconds(5)
|
||||
@location_limit_ttl :timer.seconds(1)
|
||||
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
|
||||
|
||||
def new(), do: __struct__()
|
||||
@@ -49,6 +57,90 @@ defmodule WandererApp.Character.Tracker do
|
||||
|> new()
|
||||
end
|
||||
|
||||
def check_offline(character_id) do
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:last_online_time")
|
||||
|> case do
|
||||
nil ->
|
||||
pause_tracking(character_id)
|
||||
:ok
|
||||
|
||||
last_online_time ->
|
||||
duration = DateTime.diff(DateTime.utc_now(), last_online_time, :millisecond)
|
||||
|
||||
if duration >= @offline_timeout do
|
||||
pause_tracking(character_id)
|
||||
|
||||
:ok
|
||||
else
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_online_errors(character_id),
|
||||
do: check_tracking_errors(character_id, "online", @online_error_timeout)
|
||||
|
||||
def check_ship_errors(character_id),
|
||||
do: check_tracking_errors(character_id, "ship", @ship_error_timeout)
|
||||
|
||||
def check_location_errors(character_id),
|
||||
do: check_tracking_errors(character_id, "location", @location_error_timeout)
|
||||
|
||||
defp check_tracking_errors(character_id, type, timeout) do
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:#{type}_error_time")
|
||||
|> case do
|
||||
nil ->
|
||||
:skip
|
||||
|
||||
error_time ->
|
||||
duration = DateTime.diff(DateTime.utc_now(), error_time, :millisecond)
|
||||
|
||||
if duration >= timeout do
|
||||
pause_tracking(character_id)
|
||||
|
||||
:ok
|
||||
else
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp pause_tracking(character_id) do
|
||||
if not WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused") do
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_error_time")
|
||||
WandererApp.Character.update_character(character_id, %{online: false})
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, %{
|
||||
is_online: false
|
||||
})
|
||||
|
||||
Logger.warning("[CharacterTracker] paused for #{character_id}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:tracking_paused",
|
||||
true,
|
||||
ttl: @pause_tracking_timeout
|
||||
)
|
||||
|
||||
{:ok, %{solar_system_id: solar_system_id}} =
|
||||
WandererApp.Character.get_character(character_id)
|
||||
|
||||
{:ok, %{active_maps: active_maps}} =
|
||||
WandererApp.Character.get_character_state(character_id)
|
||||
|
||||
active_maps
|
||||
|> Enum.each(fn map_id ->
|
||||
WandererApp.Cache.put(
|
||||
"map:#{map_id}:character:#{character_id}:start_solar_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
def update_settings(character_id, track_settings) do
|
||||
{:ok, character_state} = WandererApp.Character.get_character_state(character_id)
|
||||
|
||||
@@ -61,8 +153,142 @@ defmodule WandererApp.Character.Tracker do
|
||||
|> maybe_start_ship_tracking(track_settings)}
|
||||
end
|
||||
|
||||
def update_online(character_id) when is_binary(character_id),
|
||||
do:
|
||||
character_id
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_online()
|
||||
|
||||
def update_online(%{track_online: true, character_id: character_id} = character_state) do
|
||||
case WandererApp.Character.get_character(character_id) do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token}}
|
||||
when not is_nil(access_token) ->
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
|
||||
_ ->
|
||||
case WandererApp.Esi.get_character_online(eve_id,
|
||||
access_token: access_token,
|
||||
character_id: character_id
|
||||
) do
|
||||
{:ok, online} ->
|
||||
online = get_online(online)
|
||||
|
||||
if online.online == true do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:last_online_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
else
|
||||
WandererApp.Cache.delete("character:#{character_id}:last_online_time")
|
||||
end
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:info_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:wallet_forbidden")
|
||||
WandererApp.Character.update_character(character_id, online)
|
||||
|
||||
update = %{
|
||||
character_state
|
||||
| is_online: online.online,
|
||||
track_ship: online.online,
|
||||
track_location: online.online
|
||||
}
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, update)
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
Logger.warning("#{__MODULE__} failed to update_online: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
ttl: @online_forbidden_ttl
|
||||
)
|
||||
|
||||
if is_nil(
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:online_error_time")
|
||||
) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:online_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to update_online: #{inspect(:error_limited)}"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
ttl: reset_timeout
|
||||
)
|
||||
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("#{__MODULE__} failed to update_online: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
ttl: @online_forbidden_ttl
|
||||
)
|
||||
|
||||
if is_nil(
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:online_error_time")
|
||||
) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:online_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, :skipped}
|
||||
|
||||
_ ->
|
||||
{:error, :skipped}
|
||||
end
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, :skipped}
|
||||
end
|
||||
end
|
||||
|
||||
def update_online(_), do: {:error, :skipped}
|
||||
|
||||
defp get_reset_timeout(_headers, _default_timeout \\ @limit_ttl)
|
||||
|
||||
defp get_reset_timeout(
|
||||
%{"x-esi-error-limit-remain" => ["0"], "x-esi-error-limit-reset" => [reset_seconds]},
|
||||
_default_timeout
|
||||
)
|
||||
when is_binary(reset_seconds),
|
||||
do: :timer.seconds((reset_seconds |> String.to_integer()) + 1)
|
||||
|
||||
defp get_reset_timeout(_headers, default_timeout), do: default_timeout
|
||||
|
||||
def update_info(character_id) do
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:info_forbidden")
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:info_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
@@ -79,7 +305,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
Logger.warning("#{__MODULE__} failed to get_character_info: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
@@ -90,18 +316,42 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
{:error, error}
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to get_character_info: #{inspect(:error_limited)}"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:info_forbidden",
|
||||
true,
|
||||
ttl: reset_timeout
|
||||
)
|
||||
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:info_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
Logger.error("#{__MODULE__} failed to get_character_info: #{inspect(error)}")
|
||||
{:error, error}
|
||||
|
||||
_ ->
|
||||
{:error, :skipped}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_ship(character_id) when is_binary(character_id) do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_ship()
|
||||
end
|
||||
def update_ship(character_id) when is_binary(character_id),
|
||||
do:
|
||||
character_id
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_ship()
|
||||
|
||||
def update_ship(
|
||||
%{character_id: character_id, track_ship: true, is_online: true} = character_state
|
||||
@@ -110,7 +360,9 @@ defmodule WandererApp.Character.Tracker do
|
||||
|> WandererApp.Character.get_character()
|
||||
|> case do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) ->
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:ship_forbidden")
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:ship_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
@@ -118,15 +370,14 @@ defmodule WandererApp.Character.Tracker do
|
||||
_ ->
|
||||
case WandererApp.Esi.get_character_ship(eve_id,
|
||||
access_token: access_token,
|
||||
character_id: character_id,
|
||||
refresh_token?: true
|
||||
character_id: character_id
|
||||
) do
|
||||
{:ok, ship} when is_non_struct_map(ship) ->
|
||||
character_state |> maybe_update_ship(ship)
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
Logger.warning("#{__MODULE__} failed to update_ship: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
@@ -135,8 +386,28 @@ defmodule WandererApp.Character.Tracker do
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
if is_nil(WandererApp.Cache.lookup!("character:#{character_id}:ship_error_time")) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:ship_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, error}
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning("#{__MODULE__} failed to update_ship: #{inspect(:error_limited)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:ship_forbidden",
|
||||
true,
|
||||
ttl: reset_timeout
|
||||
)
|
||||
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("#{__MODULE__} failed to update_ship: #{inspect(error)}")
|
||||
|
||||
@@ -146,11 +417,31 @@ defmodule WandererApp.Character.Tracker do
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
if is_nil(WandererApp.Cache.lookup!("character:#{character_id}:ship_error_time")) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:ship_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, error}
|
||||
|
||||
_ ->
|
||||
Logger.error("#{__MODULE__} failed to update_ship: wrong response")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:ship_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
if is_nil(WandererApp.Cache.lookup!("character:#{character_id}:ship_error_time")) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:ship_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, :skipped}
|
||||
end
|
||||
end
|
||||
@@ -162,18 +453,19 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
def update_ship(_), do: {:error, :skipped}
|
||||
|
||||
def update_location(character_id) when is_binary(character_id) do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_location()
|
||||
end
|
||||
def update_location(character_id) when is_binary(character_id),
|
||||
do:
|
||||
character_id
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_location()
|
||||
|
||||
def update_location(
|
||||
%{track_location: true, is_online: true, character_id: character_id} = character_state
|
||||
) do
|
||||
case WandererApp.Character.get_character(character_id) do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) ->
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:location_forbidden")
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:location_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
@@ -181,8 +473,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
_ ->
|
||||
case WandererApp.Esi.get_character_location(eve_id,
|
||||
access_token: access_token,
|
||||
character_id: character_id,
|
||||
refresh_token?: true
|
||||
character_id: character_id
|
||||
) do
|
||||
{:ok, location} when is_non_struct_map(location) ->
|
||||
character_state
|
||||
@@ -190,31 +481,61 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
Logger.warning("#{__MODULE__} failed to update_location: #{inspect(error)}")
|
||||
|
||||
if is_nil(
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:location_error_time")
|
||||
) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:location_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to update_location: #{inspect(:error_limited)}"
|
||||
)
|
||||
|
||||
reset_timeout = get_reset_timeout(headers, @location_limit_ttl)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:location_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
ttl: reset_timeout
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("#{__MODULE__} failed to update_location: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:location_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
if is_nil(
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:location_error_time")
|
||||
) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:location_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, error}
|
||||
{:error, :skipped}
|
||||
|
||||
_ ->
|
||||
Logger.error("#{__MODULE__} failed to update_location: wrong response")
|
||||
|
||||
if is_nil(
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:location_error_time")
|
||||
) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:location_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
{:error, :skipped}
|
||||
end
|
||||
|
||||
@@ -229,116 +550,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
def update_location(_), do: {:error, :skipped}
|
||||
|
||||
def update_online(character_id) when is_binary(character_id) do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_online()
|
||||
end
|
||||
|
||||
def update_online(%{track_online: true, character_id: character_id} = character_state) do
|
||||
case WandererApp.Character.get_character(character_id) do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token}}
|
||||
when not is_nil(access_token) ->
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden")
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
|
||||
_ ->
|
||||
case WandererApp.Esi.get_character_online(eve_id,
|
||||
access_token: access_token,
|
||||
character_id: character_id,
|
||||
refresh_token?: true
|
||||
) do
|
||||
{:ok, online} ->
|
||||
online = get_online(online)
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
|
||||
WandererApp.Character.update_character(character_id, online)
|
||||
|
||||
update = %{
|
||||
character_state
|
||||
| is_online: online.online,
|
||||
track_ship: online.online,
|
||||
track_location: online.online
|
||||
}
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, update)
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
|
||||
Logger.warning("#{__MODULE__} failed to update_online: #{inspect(error)}")
|
||||
|
||||
if not WandererApp.Cache.lookup!(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
false
|
||||
) do
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
if is_nil(
|
||||
WandererApp.Cache.lookup("character:#{character_id}:online_error_time")
|
||||
) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:online_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("#{__MODULE__} failed to update_online: #{inspect(error)}")
|
||||
|
||||
if is_nil(WandererApp.Cache.lookup("character:#{character_id}:online_error_time")) do
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:online_error_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, :skipped}
|
||||
end
|
||||
end
|
||||
|
||||
def update_online(_), do: {:error, :skipped}
|
||||
|
||||
def check_online_errors(character_id) do
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:online_error_time")
|
||||
|> case do
|
||||
nil ->
|
||||
:skip
|
||||
|
||||
error_time ->
|
||||
duration = DateTime.diff(DateTime.utc_now(), error_time, :millisecond)
|
||||
|
||||
if duration >= @online_error_timeout do
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
|
||||
WandererApp.Character.update_character(character_id, %{online: false})
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, %{
|
||||
is_online: false
|
||||
})
|
||||
|
||||
:ok
|
||||
else
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_wallet(character_id) do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character()
|
||||
@@ -349,7 +560,9 @@ defmodule WandererApp.Character.Tracker do
|
||||
|> WandererApp.Character.can_track_wallet?()
|
||||
|> case do
|
||||
true ->
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:wallet_forbidden")
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:wallet_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
@@ -358,8 +571,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
case WandererApp.Esi.get_character_wallet(eve_id,
|
||||
params: %{datasource: "tranquility"},
|
||||
access_token: access_token,
|
||||
character_id: character_id,
|
||||
refresh_token?: true
|
||||
character_id: character_id
|
||||
) do
|
||||
{:ok, result} ->
|
||||
{:ok, state} = WandererApp.Character.get_character_state(character_id)
|
||||
@@ -367,8 +579,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error}
|
||||
when error in [:forbidden, :error_limited, :not_found, :timeout] ->
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
Logger.warning("#{__MODULE__} failed to update_wallet: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
@@ -377,11 +588,44 @@ defmodule WandererApp.Character.Tracker do
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to update_wallet: #{inspect(:error_limited)}"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:wallet_forbidden",
|
||||
true,
|
||||
ttl: reset_timeout
|
||||
)
|
||||
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("#{__MODULE__} failed to _update_wallet: #{inspect(error)}")
|
||||
{:error, error}
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:wallet_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
{:error, :skipped}
|
||||
|
||||
error ->
|
||||
Logger.error("#{__MODULE__} failed to _update_wallet: #{inspect(error)}")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:wallet_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
{:error, :skipped}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -395,83 +639,99 @@ defmodule WandererApp.Character.Tracker do
|
||||
end
|
||||
|
||||
defp update_alliance(%{character_id: character_id} = state, alliance_id) do
|
||||
alliance_id
|
||||
|> WandererApp.Esi.get_alliance_info()
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
{:ok, %{"name" => alliance_name, "ticker" => alliance_ticker}} ->
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
|
||||
character_update = %{
|
||||
alliance_id: alliance_id,
|
||||
alliance_name: alliance_name,
|
||||
alliance_ticker: alliance_ticker
|
||||
}
|
||||
|
||||
{:ok, _character} =
|
||||
WandererApp.Api.Character.update_alliance(character, character_update)
|
||||
|
||||
WandererApp.Character.update_character(character_id, character_update)
|
||||
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}:alliance",
|
||||
{:character_alliance, {character_id, character_update}}
|
||||
)
|
||||
|
||||
true ->
|
||||
state
|
||||
|
||||
_error ->
|
||||
Logger.error("Failed to get alliance info for #{alliance_id}")
|
||||
state
|
||||
_ ->
|
||||
alliance_id
|
||||
|> WandererApp.Esi.get_alliance_info()
|
||||
|> case do
|
||||
{:ok, %{"name" => alliance_name, "ticker" => alliance_ticker}} ->
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
|
||||
character_update = %{
|
||||
alliance_id: alliance_id,
|
||||
alliance_name: alliance_name,
|
||||
alliance_ticker: alliance_ticker
|
||||
}
|
||||
|
||||
{:ok, _character} =
|
||||
WandererApp.Api.Character.update_alliance(character, character_update)
|
||||
|
||||
WandererApp.Character.update_character(character_id, character_update)
|
||||
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}:alliance",
|
||||
{:character_alliance, {character_id, character_update}}
|
||||
)
|
||||
|
||||
state
|
||||
|
||||
_error ->
|
||||
Logger.error("Failed to get alliance info for #{alliance_id}")
|
||||
state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp update_corporation(%{character_id: character_id} = state, corporation_id) do
|
||||
corporation_id
|
||||
|> WandererApp.Esi.get_corporation_info()
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
{:ok, %{"name" => corporation_name, "ticker" => corporation_ticker} = corporation_info} ->
|
||||
alliance_id = Map.get(corporation_info, "alliance_id")
|
||||
true ->
|
||||
state
|
||||
|
||||
{:ok, character} =
|
||||
WandererApp.Character.get_character(character_id)
|
||||
_ ->
|
||||
corporation_id
|
||||
|> WandererApp.Esi.get_corporation_info()
|
||||
|> case do
|
||||
{:ok, %{"name" => corporation_name, "ticker" => corporation_ticker} = corporation_info} ->
|
||||
alliance_id = Map.get(corporation_info, "alliance_id")
|
||||
|
||||
character_update = %{
|
||||
corporation_id: corporation_id,
|
||||
corporation_name: corporation_name,
|
||||
corporation_ticker: corporation_ticker,
|
||||
alliance_id: alliance_id
|
||||
}
|
||||
{:ok, character} =
|
||||
WandererApp.Character.get_character(character_id)
|
||||
|
||||
{:ok, _character} =
|
||||
WandererApp.Api.Character.update_corporation(character, character_update)
|
||||
|
||||
WandererApp.Character.update_character(character_id, character_update)
|
||||
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}:corporation",
|
||||
{:character_corporation,
|
||||
{character_id,
|
||||
%{
|
||||
character_update = %{
|
||||
corporation_id: corporation_id,
|
||||
corporation_name: corporation_name,
|
||||
corporation_ticker: corporation_ticker
|
||||
}}}
|
||||
)
|
||||
corporation_ticker: corporation_ticker,
|
||||
alliance_id: alliance_id
|
||||
}
|
||||
|
||||
state
|
||||
|> Map.merge(%{alliance_id: alliance_id, corporation_id: corporation_id})
|
||||
|> maybe_update_alliance()
|
||||
{:ok, _character} =
|
||||
WandererApp.Api.Character.update_corporation(character, character_update)
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"Failed to get corporation info for character #{character_id}: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
corporation_id: corporation_id
|
||||
)
|
||||
WandererApp.Character.update_character(character_id, character_update)
|
||||
|
||||
state
|
||||
@pubsub_client.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}:corporation",
|
||||
{:character_corporation,
|
||||
{character_id,
|
||||
%{
|
||||
corporation_id: corporation_id,
|
||||
corporation_name: corporation_name,
|
||||
corporation_ticker: corporation_ticker
|
||||
}}}
|
||||
)
|
||||
|
||||
state
|
||||
|> Map.merge(%{alliance_id: alliance_id, corporation_id: corporation_id})
|
||||
|> maybe_update_alliance()
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"Failed to get corporation info for character #{character_id}: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
corporation_id: corporation_id
|
||||
)
|
||||
|
||||
state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -541,13 +801,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
state
|
||||
end
|
||||
|
||||
# defp is_location_started?(character_id),
|
||||
# do:
|
||||
# WandererApp.Cache.lookup!(
|
||||
# "character:#{character_id}:location_started",
|
||||
# false
|
||||
# )
|
||||
|
||||
defp is_location_updated?(
|
||||
%{
|
||||
solar_system_id: new_solar_system_id,
|
||||
|
||||
@@ -119,6 +119,11 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
{:ok, character_state} =
|
||||
WandererApp.Character.Tracker.update_settings(character_id, track_settings)
|
||||
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:last_online_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, character_state)
|
||||
else
|
||||
WandererApp.Cache.insert_or_update(
|
||||
|
||||
@@ -16,14 +16,19 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
@registry :tracker_pool_registry
|
||||
@unique_registry :unique_tracker_pool_registry
|
||||
|
||||
@update_location_interval :timer.seconds(2)
|
||||
@update_location_interval :timer.seconds(1)
|
||||
@update_online_interval :timer.seconds(5)
|
||||
@check_online_errors_interval :timer.seconds(30)
|
||||
@check_offline_characters_interval :timer.minutes(2)
|
||||
@check_online_errors_interval :timer.minutes(1)
|
||||
@check_ship_errors_interval :timer.minutes(1)
|
||||
@check_location_errors_interval :timer.minutes(1)
|
||||
@update_ship_interval :timer.seconds(2)
|
||||
@update_info_interval :timer.minutes(1)
|
||||
@update_wallet_interval :timer.minutes(1)
|
||||
@inactive_character_timeout :timer.minutes(5)
|
||||
|
||||
@pause_tracking_timeout :timer.minutes(60 * 24)
|
||||
|
||||
@logger Application.compile_env(:wanderer_app, :logger)
|
||||
|
||||
def new(), do: __struct__()
|
||||
@@ -50,6 +55,12 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
|
||||
tracked_ids
|
||||
|> Enum.each(fn id ->
|
||||
# WandererApp.Cache.put(
|
||||
# "character:#{id}:tracking_paused",
|
||||
# true,
|
||||
# ttl: @pause_tracking_timeout
|
||||
# )
|
||||
|
||||
Cachex.put(@cache, id, uuid)
|
||||
end)
|
||||
|
||||
@@ -77,6 +88,12 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
[tracked_id | r_tracked_ids]
|
||||
end)
|
||||
|
||||
# WandererApp.Cache.put(
|
||||
# "character:#{tracked_id}:tracking_paused",
|
||||
# true,
|
||||
# ttl: @pause_tracking_timeout
|
||||
# )
|
||||
|
||||
# Cachex.get_and_update(@cache, :tracked_characters, fn ids ->
|
||||
# {:commit, ids ++ [tracked_id]}
|
||||
# end)
|
||||
@@ -116,7 +133,10 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
)
|
||||
|
||||
Process.send_after(self(), :update_online, 100)
|
||||
Process.send_after(self(), :check_online_errors, @check_online_errors_interval)
|
||||
Process.send_after(self(), :check_online_errors, :timer.seconds(60))
|
||||
Process.send_after(self(), :check_ship_errors, :timer.seconds(90))
|
||||
Process.send_after(self(), :check_location_errors, :timer.seconds(120))
|
||||
Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
|
||||
Process.send_after(self(), :update_location, 300)
|
||||
Process.send_after(self(), :update_ship, 500)
|
||||
Process.send_after(self(), :update_info, 1500)
|
||||
@@ -203,6 +223,46 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_offline_characters,
|
||||
%{
|
||||
characters: characters
|
||||
} =
|
||||
state
|
||||
) do
|
||||
Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_offline,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, _result} -> :ok
|
||||
{:error, reason} -> @logger.error("Error in check_offline: #{inspect(reason)}")
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Tracker Pool] check_offline => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_online_errors,
|
||||
%{
|
||||
@@ -243,6 +303,86 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_ship_errors,
|
||||
%{
|
||||
characters: characters
|
||||
} =
|
||||
state
|
||||
) do
|
||||
Process.send_after(self(), :check_ship_errors, @check_ship_errors_interval)
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_ship_errors,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, _result} -> :ok
|
||||
{:error, reason} -> @logger.error("Error in check_ship_errors: #{inspect(reason)}")
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Tracker Pool] check_ship_errors => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_location_errors,
|
||||
%{
|
||||
characters: characters
|
||||
} =
|
||||
state
|
||||
) do
|
||||
Process.send_after(self(), :check_location_errors, @check_location_errors_interval)
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_location_errors,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, _result} -> :ok
|
||||
{:error, reason} -> @logger.error("Error in check_location_errors: #{inspect(reason)}")
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Tracker Pool] check_location_errors => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:update_location,
|
||||
%{
|
||||
|
||||
@@ -7,7 +7,7 @@ defmodule WandererApp.Character.TrackerPoolDynamicSupervisor do
|
||||
@cache :tracked_characters
|
||||
@registry :tracker_pool_registry
|
||||
@unique_registry :unique_tracker_pool_registry
|
||||
@tracker_pool_limit 100
|
||||
@tracker_pool_limit 50
|
||||
|
||||
@name __MODULE__
|
||||
|
||||
|
||||
@@ -145,13 +145,15 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
end
|
||||
|
||||
defp get_wallet_journal(
|
||||
%{corporation_id: corporation_id, access_token: access_token} = _character,
|
||||
%{id: character_id, corporation_id: corporation_id, access_token: access_token} =
|
||||
_character,
|
||||
division
|
||||
)
|
||||
when not is_nil(access_token) do
|
||||
case WandererApp.Esi.get_corporation_wallet_journal(corporation_id, division,
|
||||
params: %{datasource: "tranquility"},
|
||||
access_token: access_token
|
||||
access_token: access_token,
|
||||
character_id: character_id
|
||||
) do
|
||||
{:ok, result} ->
|
||||
{:corporation_wallet_journal, result}
|
||||
@@ -160,7 +162,7 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
Logger.warning("#{__MODULE__} failed to get_wallet_journal: forbidden")
|
||||
{:error, :forbidden}
|
||||
|
||||
{:error, :error_limited} ->
|
||||
{:error, :error_limited, _headers} ->
|
||||
Logger.warning("#{__MODULE__} failed to get_wallet_journal: error_limited")
|
||||
{:error, :error_limited}
|
||||
|
||||
@@ -173,12 +175,14 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
defp get_wallet_journal(_character, _division), do: {:error, :skipped}
|
||||
|
||||
defp update_corp_wallets(
|
||||
%{corporation_id: corporation_id, access_token: access_token} = _character
|
||||
%{id: character_id, corporation_id: corporation_id, access_token: access_token} =
|
||||
_character
|
||||
)
|
||||
when not is_nil(access_token) do
|
||||
case WandererApp.Esi.get_corporation_wallets(corporation_id,
|
||||
params: %{datasource: "tranquility"},
|
||||
access_token: access_token
|
||||
access_token: access_token,
|
||||
character_id: character_id
|
||||
) do
|
||||
{:ok, result} ->
|
||||
{:corporation_wallets, result}
|
||||
@@ -187,7 +191,7 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
Logger.warning("#{__MODULE__} failed to update_corp_wallets: forbidden")
|
||||
{:error, :forbidden}
|
||||
|
||||
{:error, :error_limited} ->
|
||||
{:error, :error_limited, _headers} ->
|
||||
Logger.warning("#{__MODULE__} failed to update_corp_wallets: error_limited")
|
||||
{:error, :error_limited}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
@default_avoid_systems [@zarzakh_system]
|
||||
|
||||
@cache_opts [cache: true]
|
||||
@retry_opts [max_retries: 0, retry_log_level: :warning]
|
||||
@retry_opts [retry: false, retry_log_level: :warning]
|
||||
@timeout_opts [pool_timeout: 15_000, receive_timeout: :timer.minutes(1)]
|
||||
@api_retry_count 1
|
||||
|
||||
@@ -275,10 +275,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
"success" => true
|
||||
}
|
||||
|
||||
{:error, :not_found} ->
|
||||
%{"origin" => origin, "destination" => destination, "systems" => [], "success" => false}
|
||||
|
||||
{:error, error} ->
|
||||
error ->
|
||||
Logger.warning("Error getting routes: #{inspect(error)}")
|
||||
%{"origin" => origin, "destination" => destination, "systems" => [], "success" => false}
|
||||
end
|
||||
@@ -293,6 +290,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
case _get_alliance_info(eve_id, "", opts) do
|
||||
{:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)}
|
||||
{:error, error} -> {:error, error}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -314,6 +312,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
case _get_corporation_info(eve_id, "", opts) do
|
||||
{:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)}
|
||||
{:error, error} -> {:error, error}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -330,6 +329,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
) do
|
||||
{:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)}
|
||||
{:error, error} -> {:error, error}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -463,10 +463,10 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
get(
|
||||
path,
|
||||
auth_opts,
|
||||
opts
|
||||
opts |> with_refresh_token()
|
||||
)
|
||||
else
|
||||
get_retry(path, auth_opts, opts)
|
||||
get_retry(path, auth_opts, opts |> with_refresh_token())
|
||||
end
|
||||
end
|
||||
|
||||
@@ -485,7 +485,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
"/corporations/#{corporation_eve_id}/#{info_path}",
|
||||
[params: opts[:params] || []] ++
|
||||
(opts |> get_auth_opts()),
|
||||
opts ++ @cache_opts
|
||||
(opts |> with_refresh_token()) ++ @cache_opts
|
||||
)
|
||||
|
||||
defp with_user_agent_opts(opts) do
|
||||
@@ -495,6 +495,10 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
)
|
||||
end
|
||||
|
||||
defp with_refresh_token(opts) do
|
||||
opts |> Keyword.merge(refresh_token?: true)
|
||||
end
|
||||
|
||||
defp with_cache_opts(opts) do
|
||||
opts |> Keyword.merge(@cache_opts) |> Keyword.merge(cache_dir: System.tmp_dir!())
|
||||
end
|
||||
@@ -531,13 +535,13 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:ok, %{status: 404}} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("#{path} error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited, headers}
|
||||
|
||||
{:ok, %{status: status} = _error} when status in [401, 403] ->
|
||||
get_retry(path, api_opts, opts)
|
||||
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited}
|
||||
|
||||
{:ok, %{status: status}} ->
|
||||
{:error, "Unexpected status: #{status}"}
|
||||
|
||||
@@ -588,8 +592,9 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:ok, %{status: 403}} ->
|
||||
{:error, :forbidden}
|
||||
|
||||
{:ok, %{status: 420}} ->
|
||||
{:error, :error_limited}
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("#{url} error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited, headers}
|
||||
|
||||
{:ok, %{status: status}} ->
|
||||
{:error, "Unexpected status: #{status}"}
|
||||
@@ -608,8 +613,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
defp post_esi(url, opts) do
|
||||
try do
|
||||
req_opts =
|
||||
(opts
|
||||
|> with_user_agent_opts()) ++
|
||||
(opts |> with_user_agent_opts() |> Keyword.merge(@retry_opts)) ++
|
||||
[params: opts[:params] || []]
|
||||
|
||||
Req.new(
|
||||
@@ -627,8 +631,9 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:ok, %{status: 403}} ->
|
||||
{:error, :forbidden}
|
||||
|
||||
{:ok, %{status: 420}} ->
|
||||
{:error, :error_limited}
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("#{url} error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited, headers}
|
||||
|
||||
{:ok, %{status: status}} ->
|
||||
{:error, "Unexpected status: #{status}"}
|
||||
|
||||
@@ -219,10 +219,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
Task.start_link(fn ->
|
||||
character_updates =
|
||||
maybe_update_online(map_id, character_id) ++
|
||||
maybe_update_location(map_id, character_id) ++
|
||||
maybe_update_ship(map_id, character_id) ++
|
||||
maybe_update_alliance(map_id, character_id) ++
|
||||
maybe_update_corporation(map_id, character_id)
|
||||
maybe_update_tracking_status(map_id, character_id)
|
||||
|
||||
maybe_update_location(map_id, character_id) ++
|
||||
maybe_update_ship(map_id, character_id) ++
|
||||
maybe_update_alliance(map_id, character_id) ++
|
||||
maybe_update_corporation(map_id, character_id)
|
||||
|
||||
character_updates
|
||||
|> Enum.filter(fn update -> update != :skip end)
|
||||
@@ -245,6 +247,9 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
{:character_online, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_tracking, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_alliance, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
@@ -388,6 +393,33 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_tracking_status(map_id, character_id) do
|
||||
with {:ok, old_tracking_paused} <-
|
||||
WandererApp.Cache.lookup(
|
||||
"map:#{map_id}:character:#{character_id}:tracking_paused",
|
||||
false
|
||||
),
|
||||
{:ok, tracking_paused} <-
|
||||
WandererApp.Cache.lookup("character:#{character_id}:tracking_paused", false) do
|
||||
case old_tracking_paused != tracking_paused do
|
||||
true ->
|
||||
WandererApp.Cache.insert(
|
||||
"map:#{map_id}:character:#{character_id}:tracking_paused",
|
||||
tracking_paused
|
||||
)
|
||||
|
||||
[{:character_tracking, %{tracking_paused: tracking_paused}}]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to update character_tracking: #{inspect(error, pretty: true)}")
|
||||
[:skip]
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_update_ship(map_id, character_id) do
|
||||
with {:ok, old_ship_type_id} <-
|
||||
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:ship_type_id"),
|
||||
|
||||
@@ -18,34 +18,38 @@ defmodule WandererApp.Maps do
|
||||
]
|
||||
|
||||
def find_routes(map_id, hubs, origin, routes_settings, false) do
|
||||
{:ok, routes} =
|
||||
WandererApp.Esi.find_routes(
|
||||
map_id,
|
||||
origin,
|
||||
hubs,
|
||||
routes_settings
|
||||
)
|
||||
WandererApp.Esi.find_routes(
|
||||
map_id,
|
||||
origin,
|
||||
hubs,
|
||||
routes_settings
|
||||
)
|
||||
|> case do
|
||||
{:ok, routes} ->
|
||||
systems_static_data =
|
||||
routes
|
||||
|> Enum.map(fn route_info -> route_info.systems end)
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
|> Task.async_stream(
|
||||
fn system_id ->
|
||||
case WandererApp.CachedInfo.get_system_static_info(system_id) do
|
||||
{:ok, nil} ->
|
||||
nil
|
||||
|
||||
systems_static_data =
|
||||
routes
|
||||
|> Enum.map(fn route_info -> route_info.systems end)
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
|> Task.async_stream(
|
||||
fn system_id ->
|
||||
case WandererApp.CachedInfo.get_system_static_info(system_id) do
|
||||
{:ok, nil} ->
|
||||
nil
|
||||
{:ok, system} ->
|
||||
system |> Map.take(@minimum_route_attrs)
|
||||
end
|
||||
end,
|
||||
max_concurrency: 10
|
||||
)
|
||||
|> Enum.map(fn {:ok, val} -> val end)
|
||||
|
||||
{:ok, system} ->
|
||||
system |> Map.take(@minimum_route_attrs)
|
||||
end
|
||||
end,
|
||||
max_concurrency: 10
|
||||
)
|
||||
|> Enum.map(fn {:ok, val} -> val end)
|
||||
{:ok, %{routes: routes, systems_static_data: systems_static_data}}
|
||||
|
||||
{:ok, %{routes: routes, systems_static_data: systems_static_data}}
|
||||
error ->
|
||||
{:ok, %{routes: [], systems_static_data: []}}
|
||||
end
|
||||
end
|
||||
|
||||
def find_routes(map_id, hubs, origin, routes_settings, true) do
|
||||
|
||||
@@ -28,8 +28,6 @@ defmodule WandererApp.Server.ServerStatusTracker do
|
||||
|
||||
@logger Application.compile_env(:wanderer_app, :logger)
|
||||
|
||||
def get_status(), do: GenServer.call(@name, :get_status)
|
||||
|
||||
def start_link(opts \\ []), do: GenServer.start(__MODULE__, opts, name: @name)
|
||||
|
||||
@impl true
|
||||
@@ -42,9 +40,6 @@ defmodule WandererApp.Server.ServerStatusTracker do
|
||||
@impl true
|
||||
def terminate(_reason, _state), do: :ok
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_status, _from, state), do: {:reply, {:ok, state}, state}
|
||||
|
||||
@impl true
|
||||
def handle_call(:stop, _, state), do: {:stop, :normal, :ok, state}
|
||||
|
||||
@@ -66,7 +61,7 @@ defmodule WandererApp.Server.ServerStatusTracker do
|
||||
} = state
|
||||
) do
|
||||
Process.send_after(self(), :refresh_status, @refresh_interval)
|
||||
Task.async(fn -> _get_server_status(retries) end)
|
||||
Task.async(fn -> get_server_status(retries) end)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
@@ -104,7 +99,7 @@ defmodule WandererApp.Server.ServerStatusTracker do
|
||||
def handle_info(_action, state),
|
||||
do: {:noreply, state}
|
||||
|
||||
defp _get_server_status(retries) do
|
||||
defp get_server_status(retries) do
|
||||
case WandererApp.Esi.get_server_status() do
|
||||
{:ok, result} ->
|
||||
{:status, _get_status(result)}
|
||||
@@ -113,7 +108,7 @@ defmodule WandererApp.Server.ServerStatusTracker do
|
||||
if retries > 0 do
|
||||
:retry
|
||||
else
|
||||
@logger.warning("#{__MODULE__} failed to refresh server status: :timeout")
|
||||
Logger.warning("#{__MODULE__} failed to refresh server status: :timeout")
|
||||
{:status, @initial_state}
|
||||
end
|
||||
|
||||
@@ -121,9 +116,12 @@ defmodule WandererApp.Server.ServerStatusTracker do
|
||||
if retries > 0 do
|
||||
:retry
|
||||
else
|
||||
@logger.warning("#{__MODULE__} failed to refresh server status: #{inspect(error)}")
|
||||
Logger.warning("#{__MODULE__} failed to refresh server status: #{inspect(error)}")
|
||||
{:status, @initial_state}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, :unknown}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -15,26 +15,39 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
@doc """
|
||||
Fetch killmails for multiple systems, returning a map of system_id => kills.
|
||||
"""
|
||||
def fetch_kills_for_systems(system_ids, since_hours, state, _opts \\ []) when is_list(system_ids) do
|
||||
try do
|
||||
{final_map, final_state} =
|
||||
Enum.reduce(system_ids, {%{}, state}, fn sid, {acc_map, acc_st} ->
|
||||
case fetch_kills_for_system(sid, since_hours, acc_st) do
|
||||
{:ok, kills, new_st} ->
|
||||
{Map.put(acc_map, sid, kills), new_st}
|
||||
def fetch_kills_for_systems(system_ids, since_hours, state, _opts \\ [])
|
||||
when is_list(system_ids) do
|
||||
zkill_preload_disabled = WandererApp.Env.zkill_preload_disabled?()
|
||||
|
||||
{:error, reason, new_st} ->
|
||||
Logger.debug(fn -> "[Fetcher] system=#{sid} => error=#{inspect(reason)}" end)
|
||||
{Map.put(acc_map, sid, {:error, reason}), new_st}
|
||||
end
|
||||
if not zkill_preload_disabled do
|
||||
try do
|
||||
{final_map, final_state} =
|
||||
Enum.reduce(system_ids, {%{}, state}, fn sid, {acc_map, acc_st} ->
|
||||
case fetch_kills_for_system(sid, since_hours, acc_st) do
|
||||
{:ok, kills, new_st} ->
|
||||
{Map.put(acc_map, sid, kills), new_st}
|
||||
|
||||
{:error, reason, new_st} ->
|
||||
Logger.debug(fn -> "[Fetcher] system=#{sid} => error=#{inspect(reason)}" end)
|
||||
{Map.put(acc_map, sid, {:error, reason}), new_st}
|
||||
end
|
||||
end)
|
||||
|
||||
Logger.debug(fn ->
|
||||
"[Fetcher] fetch_kills_for_systems => done, final_map_size=#{map_size(final_map)} calls=#{final_state.calls_count}"
|
||||
end)
|
||||
|
||||
Logger.debug(fn -> "[Fetcher] fetch_kills_for_systems => done, final_map_size=#{map_size(final_map)} calls=#{final_state.calls_count}" end)
|
||||
{:ok, final_map}
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("[Fetcher] EXCEPTION in fetch_kills_for_systems => #{Exception.message(e)}")
|
||||
{:error, e}
|
||||
{:ok, final_map}
|
||||
rescue
|
||||
e ->
|
||||
Logger.error(
|
||||
"[Fetcher] EXCEPTION in fetch_kills_for_systems => #{Exception.message(e)}"
|
||||
)
|
||||
|
||||
{:error, e}
|
||||
end
|
||||
else
|
||||
{:error, :kills_disabled}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,55 +62,68 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
Returns `{:ok, kills, updated_state}` on success, or `{:error, reason, updated_state}`.
|
||||
"""
|
||||
def fetch_kills_for_system(system_id, since_hours, state, opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit, nil)
|
||||
force? = Keyword.get(opts, :force, false)
|
||||
zkill_preload_disabled = WandererApp.Env.zkill_preload_disabled?()
|
||||
|
||||
log_prefix = "[Fetcher] fetch_kills_for_system => system=#{system_id}"
|
||||
if not zkill_preload_disabled do
|
||||
limit = Keyword.get(opts, :limit, nil)
|
||||
force? = Keyword.get(opts, :force, false)
|
||||
|
||||
# Check the "recently fetched" cache if not forced
|
||||
if not force? and KillsCache.recently_fetched?(system_id) do
|
||||
cached_kills = KillsCache.fetch_cached_kills(system_id)
|
||||
final = maybe_take(cached_kills, limit)
|
||||
Logger.debug(fn -> "#{log_prefix}, recently_fetched?=true => returning #{length(final)} cached kills" end)
|
||||
{:ok, final, state}
|
||||
else
|
||||
Logger.debug(fn -> "#{log_prefix}, hours=#{since_hours}, limit=#{inspect(limit)}, force=#{force?}" end)
|
||||
log_prefix = "[Fetcher] fetch_kills_for_system => system=#{system_id}"
|
||||
|
||||
cutoff_dt = hours_ago(since_hours)
|
||||
# Check the "recently fetched" cache if not forced
|
||||
if not force? and KillsCache.recently_fetched?(system_id) do
|
||||
cached_kills = KillsCache.fetch_cached_kills(system_id)
|
||||
final = maybe_take(cached_kills, limit)
|
||||
|
||||
result =
|
||||
retry with: exponential_backoff(300)
|
||||
|> randomize()
|
||||
|> cap(5_000)
|
||||
|> expiry(120_000) do
|
||||
case do_multi_page_fetch(system_id, cutoff_dt, 1, 0, limit, state) do
|
||||
{:ok, new_st, total_fetched} ->
|
||||
# Mark system as fully fetched (to prevent repeated calls).
|
||||
KillsCache.put_full_fetched_timestamp(system_id)
|
||||
final_kills = KillsCache.fetch_cached_kills(system_id) |> maybe_take(limit)
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, recently_fetched?=true => returning #{length(final)} cached kills"
|
||||
end)
|
||||
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, total_fetched=#{total_fetched}, final_cached=#{length(final_kills)}, calls_count=#{new_st.calls_count}"
|
||||
end)
|
||||
{:ok, final, state}
|
||||
else
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, hours=#{since_hours}, limit=#{inspect(limit)}, force=#{force?}"
|
||||
end)
|
||||
|
||||
{:ok, final_kills, new_st}
|
||||
cutoff_dt = hours_ago(since_hours)
|
||||
|
||||
{:error, :rate_limited, _new_st} ->
|
||||
raise ":rate_limited"
|
||||
result =
|
||||
retry with:
|
||||
exponential_backoff(300)
|
||||
|> randomize()
|
||||
|> cap(5_000)
|
||||
|> expiry(120_000) do
|
||||
case do_multi_page_fetch(system_id, cutoff_dt, 1, 0, limit, state) do
|
||||
{:ok, new_st, total_fetched} ->
|
||||
# Mark system as fully fetched (to prevent repeated calls).
|
||||
KillsCache.put_full_fetched_timestamp(system_id)
|
||||
final_kills = KillsCache.fetch_cached_kills(system_id) |> maybe_take(limit)
|
||||
|
||||
{:error, reason, _new_st} ->
|
||||
raise "#{log_prefix}, reason=#{inspect(reason)}"
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, total_fetched=#{total_fetched}, final_cached=#{length(final_kills)}, calls_count=#{new_st.calls_count}"
|
||||
end)
|
||||
|
||||
{:ok, final_kills, new_st}
|
||||
|
||||
{:error, :rate_limited, _new_st} ->
|
||||
raise ":rate_limited"
|
||||
|
||||
{:error, reason, _new_st} ->
|
||||
raise "#{log_prefix}, reason=#{inspect(reason)}"
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, kills, new_st} ->
|
||||
{:ok, kills, new_st}
|
||||
|
||||
error ->
|
||||
Logger.error("#{log_prefix}, EXHAUSTED => error=#{inspect(error)}")
|
||||
{:error, error, state}
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, kills, new_st} ->
|
||||
{:ok, kills, new_st}
|
||||
|
||||
error ->
|
||||
Logger.error("#{log_prefix}, EXHAUSTED => error=#{inspect(error)}")
|
||||
{:error, error, state}
|
||||
end
|
||||
else
|
||||
raise ":kills_disabled"
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
@@ -118,10 +144,13 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
|
||||
with {:ok, st1} <- increment_calls_count(state),
|
||||
{:ok, st2, partials} <- ZkbApi.fetch_and_parse_page(system_id, page, st1) do
|
||||
Logger.debug(fn -> "[Fetcher] system=#{system_id}, page=#{page}, partials_count=#{length(partials)}" end)
|
||||
Logger.debug(fn ->
|
||||
"[Fetcher] system=#{system_id}, page=#{page}, partials_count=#{length(partials)}"
|
||||
end)
|
||||
|
||||
{_count_stored, older_found?, total_now} =
|
||||
Enum.reduce_while(partials, {0, false, total_so_far}, fn partial, {acc_count, had_older, acc_total} ->
|
||||
Enum.reduce_while(partials, {0, false, total_so_far}, fn partial,
|
||||
{acc_count, had_older, acc_total} ->
|
||||
# If we have a limit and reached it, stop immediately
|
||||
if reached_limit?(limit, acc_total) do
|
||||
{:halt, {acc_count, had_older, acc_total}}
|
||||
@@ -170,7 +199,10 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_partial(%{"killmail_id" => kill_id, "zkb" => %{"hash" => kill_hash}} = partial, cutoff_dt) do
|
||||
defp parse_partial(
|
||||
%{"killmail_id" => kill_id, "zkb" => %{"hash" => kill_hash}} = partial,
|
||||
cutoff_dt
|
||||
) do
|
||||
# If we've already cached this kill, skip
|
||||
if KillsCache.get_killmail(kill_id) do
|
||||
:skip
|
||||
@@ -191,7 +223,8 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
defp parse_partial(_other, _cutoff_dt), do: :skip
|
||||
|
||||
defp fetch_full_killmail(k_id, k_hash) do
|
||||
retry with: exponential_backoff(300) |> randomize() |> cap(5_000) |> expiry(30_000), rescue_only: [RuntimeError] do
|
||||
retry with: exponential_backoff(300) |> randomize() |> cap(5_000) |> expiry(30_000),
|
||||
rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_killmail(k_id, k_hash) do
|
||||
{:ok, full_km} ->
|
||||
{:ok, full_km}
|
||||
@@ -206,12 +239,25 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.warning("[Fetcher] ESI get_killmail retriable error => kill_id=#{k_id}, reason=#{inspect(reason)}")
|
||||
Logger.warning(
|
||||
"[Fetcher] ESI get_killmail retriable error => kill_id=#{k_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
raise "ESI error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning("[Fetcher] ESI get_killmail failed => kill_id=#{k_id}, reason=#{inspect(reason)}")
|
||||
Logger.warning(
|
||||
"[Fetcher] ESI get_killmail failed => kill_id=#{k_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
{:error, reason}
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"[Fetcher] ESI get_killmail failed => kill_id=#{k_id}, reason=#{inspect(error)}"
|
||||
)
|
||||
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -223,6 +269,7 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
do: {:ok, %{st | calls_count: c + 1}}
|
||||
|
||||
defp reached_limit?(nil, _count_so_far), do: false
|
||||
|
||||
defp reached_limit?(limit, count_so_far) when is_integer(limit),
|
||||
do: count_so_far >= limit
|
||||
|
||||
|
||||
@@ -126,7 +126,9 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
KillsCache.incr_system_kill_count(system_id)
|
||||
end
|
||||
else
|
||||
Logger.warning("[Parser] store_killmail => build_kill_data returned nil for kill_id=#{kill_id}")
|
||||
Logger.warning(
|
||||
"[Parser] store_killmail => build_kill_data returned nil for kill_id=#{kill_id}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -144,7 +146,6 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
"total_value" => total_value,
|
||||
"npc" => npc
|
||||
}) do
|
||||
|
||||
victim_map = extract_victim_fields(victim)
|
||||
final_blow_map = extract_final_blow_fields(attackers)
|
||||
|
||||
@@ -153,17 +154,14 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
"kill_time" => kill_time_dt,
|
||||
"solar_system_id" => sys_id,
|
||||
"zkb" => zkb,
|
||||
|
||||
"victim_char_id" => victim_map.char_id,
|
||||
"victim_corp_id" => victim_map.corp_id,
|
||||
"victim_alliance_id" => victim_map.alliance_id,
|
||||
"victim_ship_type_id" => victim_map.ship_type_id,
|
||||
|
||||
"final_blow_char_id" => final_blow_map.char_id,
|
||||
"final_blow_corp_id" => final_blow_map.corp_id,
|
||||
"final_blow_alliance_id" => final_blow_map.alliance_id,
|
||||
"final_blow_ship_type_id" => final_blow_map.ship_type_id,
|
||||
|
||||
"attacker_count" => attacker_count,
|
||||
"total_value" => total_value,
|
||||
"npc" => npc
|
||||
@@ -179,14 +177,14 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
"alliance_id" => alli,
|
||||
"ship_type_id" => st_id
|
||||
}),
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: alli, ship_type_id: st_id}
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: alli, ship_type_id: st_id}
|
||||
|
||||
defp extract_victim_fields(%{
|
||||
"character_id" => cid,
|
||||
"corporation_id" => corp,
|
||||
"ship_type_id" => st_id
|
||||
}),
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: nil, ship_type_id: st_id}
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: nil, ship_type_id: st_id}
|
||||
|
||||
defp extract_victim_fields(_),
|
||||
do: %{char_id: nil, corp_id: nil, alliance_id: nil, ship_type_id: nil}
|
||||
@@ -208,14 +206,14 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
"alliance_id" => alli,
|
||||
"ship_type_id" => st_id
|
||||
}),
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: alli, ship_type_id: st_id}
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: alli, ship_type_id: st_id}
|
||||
|
||||
defp extract_attacker_fields(%{
|
||||
"character_id" => cid,
|
||||
"corporation_id" => corp,
|
||||
"ship_type_id" => st_id
|
||||
}),
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: nil, ship_type_id: st_id}
|
||||
do: %{char_id: cid, corp_id: corp, alliance_id: nil, ship_type_id: st_id}
|
||||
|
||||
defp extract_attacker_fields(%{"ship_type_id" => st_id} = attacker) do
|
||||
%{
|
||||
@@ -235,52 +233,78 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
|> enrich_final_blow()
|
||||
end
|
||||
|
||||
|
||||
defp enrich_victim(km) do
|
||||
km
|
||||
|> maybe_put_character_name("victim_char_id", "victim_char_name")
|
||||
|> maybe_put_corp_info("victim_corp_id", "victim_corp_ticker", "victim_corp_name")
|
||||
|> maybe_put_alliance_info("victim_alliance_id", "victim_alliance_ticker", "victim_alliance_name")
|
||||
|> maybe_put_alliance_info(
|
||||
"victim_alliance_id",
|
||||
"victim_alliance_ticker",
|
||||
"victim_alliance_name"
|
||||
)
|
||||
|> maybe_put_ship_name("victim_ship_type_id", "victim_ship_name")
|
||||
end
|
||||
|
||||
|
||||
defp enrich_final_blow(km) do
|
||||
km
|
||||
|> maybe_put_character_name("final_blow_char_id", "final_blow_char_name")
|
||||
|> maybe_put_corp_info("final_blow_corp_id", "final_blow_corp_ticker", "final_blow_corp_name")
|
||||
|> maybe_put_alliance_info("final_blow_alliance_id", "final_blow_alliance_ticker", "final_blow_alliance_name")
|
||||
|> maybe_put_alliance_info(
|
||||
"final_blow_alliance_id",
|
||||
"final_blow_alliance_ticker",
|
||||
"final_blow_alliance_name"
|
||||
)
|
||||
|> maybe_put_ship_name("final_blow_ship_type_id", "final_blow_ship_name")
|
||||
end
|
||||
|
||||
defp maybe_put_character_name(km, id_key, name_key) do
|
||||
case Map.get(km, id_key) do
|
||||
nil -> km
|
||||
0 -> km
|
||||
nil ->
|
||||
km
|
||||
|
||||
0 ->
|
||||
km
|
||||
|
||||
eve_id ->
|
||||
result = retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000), rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_character_info(eve_id) do
|
||||
{:ok, %{"name" => char_name}} ->
|
||||
{:ok, char_name}
|
||||
result =
|
||||
retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000),
|
||||
rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_character_info(eve_id) do
|
||||
{:ok, %{"name" => char_name}} ->
|
||||
{:ok, char_name}
|
||||
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn -> "[Parser] Character info timeout, retrying => id=#{eve_id}" end)
|
||||
raise "Character info timeout, will retry"
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn -> "[Parser] Character info timeout, retrying => id=#{eve_id}" end)
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Character not found => id=#{eve_id}" end)
|
||||
:skip
|
||||
raise "Character info timeout, will retry"
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn -> "[Parser] Character info retriable error => id=#{eve_id}, reason=#{inspect(reason)}" end)
|
||||
raise "Character info error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.debug(fn -> "[Parser] Character info failed => id=#{eve_id}, reason=#{inspect(reason)}" end)
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Character not found => id=#{eve_id}" end)
|
||||
:skip
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Character info retriable error => id=#{eve_id}, reason=#{inspect(reason)}"
|
||||
end)
|
||||
|
||||
raise "Character info error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Character info failed => id=#{eve_id}, reason=#{inspect(reason)}"
|
||||
end)
|
||||
|
||||
:skip
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Character info failed => id=#{eve_id}, reason=#{inspect(error)}"
|
||||
end)
|
||||
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, char_name} -> Map.put(km, name_key, char_name)
|
||||
@@ -291,109 +315,180 @@ defmodule WandererApp.Zkb.KillsProvider.Parser do
|
||||
|
||||
defp maybe_put_corp_info(km, id_key, ticker_key, name_key) do
|
||||
case Map.get(km, id_key) do
|
||||
nil -> km
|
||||
0 -> km
|
||||
nil ->
|
||||
km
|
||||
|
||||
0 ->
|
||||
km
|
||||
|
||||
corp_id ->
|
||||
result = retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000), rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_corporation_info(corp_id) do
|
||||
{:ok, %{"ticker" => ticker, "name" => corp_name}} ->
|
||||
{:ok, {ticker, corp_name}}
|
||||
result =
|
||||
retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000),
|
||||
rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_corporation_info(corp_id) do
|
||||
{:ok, %{"ticker" => ticker, "name" => corp_name}} ->
|
||||
{:ok, {ticker, corp_name}}
|
||||
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn -> "[Parser] Corporation info timeout, retrying => id=#{corp_id}" end)
|
||||
raise "Corporation info timeout, will retry"
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Corporation info timeout, retrying => id=#{corp_id}"
|
||||
end)
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Corporation not found => id=#{corp_id}" end)
|
||||
:skip
|
||||
raise "Corporation info timeout, will retry"
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn -> "[Parser] Corporation info retriable error => id=#{corp_id}, reason=#{inspect(reason)}" end)
|
||||
raise "Corporation info error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning("[Parser] Failed to fetch corp info: ID=#{corp_id}, reason=#{inspect(reason)}")
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Corporation not found => id=#{corp_id}" end)
|
||||
:skip
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Corporation info retriable error => id=#{corp_id}, reason=#{inspect(reason)}"
|
||||
end)
|
||||
|
||||
raise "Corporation info error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning(
|
||||
"[Parser] Failed to fetch corp info: ID=#{corp_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"[Parser] Failed to fetch corp info: ID=#{corp_id}, reason=#{inspect(error)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, {ticker, corp_name}} ->
|
||||
km
|
||||
|> Map.put(ticker_key, ticker)
|
||||
|> Map.put(name_key, corp_name)
|
||||
_ -> km
|
||||
|
||||
_ ->
|
||||
km
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_put_alliance_info(km, id_key, ticker_key, name_key) do
|
||||
case Map.get(km, id_key) do
|
||||
nil -> km
|
||||
0 -> km
|
||||
nil ->
|
||||
km
|
||||
|
||||
0 ->
|
||||
km
|
||||
|
||||
alliance_id ->
|
||||
result = retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000), rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_alliance_info(alliance_id) do
|
||||
{:ok, %{"ticker" => alliance_ticker, "name" => alliance_name}} ->
|
||||
{:ok, {alliance_ticker, alliance_name}}
|
||||
result =
|
||||
retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000),
|
||||
rescue_only: [RuntimeError] do
|
||||
case WandererApp.Esi.get_alliance_info(alliance_id) do
|
||||
{:ok, %{"ticker" => alliance_ticker, "name" => alliance_name}} ->
|
||||
{:ok, {alliance_ticker, alliance_name}}
|
||||
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn -> "[Parser] Alliance info timeout, retrying => id=#{alliance_id}" end)
|
||||
raise "Alliance info timeout, will retry"
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Alliance info timeout, retrying => id=#{alliance_id}"
|
||||
end)
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Alliance not found => id=#{alliance_id}" end)
|
||||
:skip
|
||||
raise "Alliance info timeout, will retry"
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn -> "[Parser] Alliance info retriable error => id=#{alliance_id}, reason=#{inspect(reason)}" end)
|
||||
raise "Alliance info error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.debug(fn -> "[Parser] Alliance info failed => id=#{alliance_id}, reason=#{inspect(reason)}" end)
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Alliance not found => id=#{alliance_id}" end)
|
||||
:skip
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Alliance info retriable error => id=#{alliance_id}, reason=#{inspect(reason)}"
|
||||
end)
|
||||
|
||||
raise "Alliance info error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Alliance info failed => id=#{alliance_id}, reason=#{inspect(reason)}"
|
||||
end)
|
||||
|
||||
:skip
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Alliance info failed => id=#{alliance_id}, reason=#{inspect(error)}"
|
||||
end)
|
||||
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, {alliance_ticker, alliance_name}} ->
|
||||
km
|
||||
|> Map.put(ticker_key, alliance_ticker)
|
||||
|> Map.put(name_key, alliance_name)
|
||||
_ -> km
|
||||
|
||||
_ ->
|
||||
km
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_put_ship_name(km, id_key, name_key) do
|
||||
case Map.get(km, id_key) do
|
||||
nil -> km
|
||||
0 -> km
|
||||
nil ->
|
||||
km
|
||||
|
||||
0 ->
|
||||
km
|
||||
|
||||
type_id ->
|
||||
result = retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000), rescue_only: [RuntimeError] do
|
||||
case WandererApp.CachedInfo.get_ship_type(type_id) do
|
||||
{:ok, nil} -> :skip
|
||||
{:ok, %{name: ship_name}} -> {:ok, ship_name}
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn -> "[Parser] Ship type timeout, retrying => id=#{type_id}" end)
|
||||
raise "Ship type timeout, will retry"
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Ship type not found => id=#{type_id}" end)
|
||||
:skip
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn -> "[Parser] Ship type retriable error => id=#{type_id}, reason=#{inspect(reason)}" end)
|
||||
raise "Ship type error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning("[Parser] Failed to fetch ship type: ID=#{type_id}, reason=#{inspect(reason)}")
|
||||
result =
|
||||
retry with: exponential_backoff(200) |> randomize() |> cap(2_000) |> expiry(10_000),
|
||||
rescue_only: [RuntimeError] do
|
||||
case WandererApp.CachedInfo.get_ship_type(type_id) do
|
||||
{:ok, nil} ->
|
||||
:skip
|
||||
end
|
||||
|
||||
{:ok, %{name: ship_name}} ->
|
||||
{:ok, ship_name}
|
||||
|
||||
{:error, :timeout} ->
|
||||
Logger.debug(fn -> "[Parser] Ship type timeout, retrying => id=#{type_id}" end)
|
||||
raise "Ship type timeout, will retry"
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.debug(fn -> "[Parser] Ship type not found => id=#{type_id}" end)
|
||||
:skip
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.debug(fn ->
|
||||
"[Parser] Ship type retriable error => id=#{type_id}, reason=#{inspect(reason)}"
|
||||
end)
|
||||
|
||||
raise "Ship type error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning(
|
||||
"[Parser] Failed to fetch ship type: ID=#{type_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"[Parser] Failed to fetch ship type: ID=#{type_id}, reason=#{inspect(error)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, ship_name} -> Map.put(km, name_key, ship_name)
|
||||
|
||||
@@ -46,7 +46,10 @@ defmodule WandererApp.Zkb.KillsProvider.Websocket do
|
||||
|
||||
# Called on disconnect
|
||||
def handle_disconnect(code, reason, _old_state) do
|
||||
Logger.warning("[KillsProvider.Websocket] Disconnected => code=#{code}, reason=#{inspect(reason)} => reconnecting")
|
||||
Logger.warning(
|
||||
"[KillsProvider.Websocket] Disconnected => code=#{code}, reason=#{inspect(reason)} => reconnecting"
|
||||
)
|
||||
|
||||
:reconnect
|
||||
end
|
||||
|
||||
@@ -69,41 +72,73 @@ defmodule WandererApp.Zkb.KillsProvider.Websocket do
|
||||
end
|
||||
|
||||
# The partial from zKillboard has killmail_id + zkb.hash, but no time/victim/attackers
|
||||
defp parse_and_store_zkb_partial(%{"killmail_id" => kill_id, "zkb" => %{"hash" => kill_hash}} = partial) do
|
||||
Logger.debug(fn -> "[KillsProvider.Websocket] parse_and_store_zkb_partial => kill_id=#{kill_id}" end)
|
||||
defp parse_and_store_zkb_partial(
|
||||
%{"killmail_id" => kill_id, "zkb" => %{"hash" => kill_hash}} = partial
|
||||
) do
|
||||
Logger.debug(fn ->
|
||||
"[KillsProvider.Websocket] parse_and_store_zkb_partial => kill_id=#{kill_id}"
|
||||
end)
|
||||
|
||||
result = retry with: exponential_backoff(300) |> randomize() |> cap(5_000) |> expiry(30_000), rescue_only: [RuntimeError] do
|
||||
case Esi.get_killmail(kill_id, kill_hash) do
|
||||
{:ok, full_esi_data} ->
|
||||
# Merge partial zKB fields (like totalValue) onto ESI data
|
||||
enriched = Map.merge(full_esi_data, %{"zkb" => partial["zkb"]})
|
||||
Parser.parse_and_store_killmail(enriched)
|
||||
:ok
|
||||
result =
|
||||
retry with: exponential_backoff(300) |> randomize() |> cap(5_000) |> expiry(30_000),
|
||||
rescue_only: [RuntimeError] do
|
||||
case Esi.get_killmail(kill_id, kill_hash) do
|
||||
{:ok, full_esi_data} ->
|
||||
# Merge partial zKB fields (like totalValue) onto ESI data
|
||||
enriched = Map.merge(full_esi_data, %{"zkb" => partial["zkb"]})
|
||||
Parser.parse_and_store_killmail(enriched)
|
||||
:ok
|
||||
|
||||
{:error, :timeout} ->
|
||||
Logger.warning("[KillsProvider.Websocket] ESI get_killmail timeout => kill_id=#{kill_id}, retrying...")
|
||||
raise "ESI timeout, will retry"
|
||||
{:error, :timeout} ->
|
||||
Logger.warning(
|
||||
"[KillsProvider.Websocket] ESI get_killmail timeout => kill_id=#{kill_id}, retrying..."
|
||||
)
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.warning("[KillsProvider.Websocket] ESI get_killmail not_found => kill_id=#{kill_id}")
|
||||
:skip
|
||||
raise "ESI timeout, will retry"
|
||||
|
||||
{:error, :not_found} ->
|
||||
Logger.warning(
|
||||
"[KillsProvider.Websocket] ESI get_killmail not_found => kill_id=#{kill_id}"
|
||||
)
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.warning("[KillsProvider.Websocket] ESI get_killmail retriable error => kill_id=#{kill_id}, reason=#{inspect(reason)}")
|
||||
raise "ESI error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning("[KillsProvider.Websocket] ESI get_killmail failed => kill_id=#{kill_id}, reason=#{inspect(reason)}")
|
||||
:skip
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
if HttpUtil.retriable_error?(reason) do
|
||||
Logger.warning(
|
||||
"[KillsProvider.Websocket] ESI get_killmail retriable error => kill_id=#{kill_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
raise "ESI error: #{inspect(reason)}, will retry"
|
||||
else
|
||||
Logger.warning(
|
||||
"[KillsProvider.Websocket] ESI get_killmail failed => kill_id=#{kill_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"[KillsProvider.Websocket] ESI get_killmail failed => kill_id=#{kill_id}, reason=#{inspect(error)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
:ok -> :ok
|
||||
:skip -> :skip
|
||||
:ok ->
|
||||
:ok
|
||||
|
||||
:skip ->
|
||||
:skip
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("[KillsProvider.Websocket] ESI get_killmail exhausted retries => kill_id=#{kill_id}, reason=#{inspect(reason)}")
|
||||
Logger.error(
|
||||
"[KillsProvider.Websocket] ESI get_killmail exhausted retries => kill_id=#{kill_id}, reason=#{inspect(reason)}"
|
||||
)
|
||||
|
||||
:skip
|
||||
end
|
||||
end
|
||||
|
||||
@@ -104,7 +104,7 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
Creates a new ACL member.
|
||||
"""
|
||||
@spec create(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :create,
|
||||
operation(:create,
|
||||
summary: "Create ACL Member",
|
||||
description: "Creates a new ACL member for a given ACL.",
|
||||
parameters: [
|
||||
@@ -127,6 +127,8 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
@acl_member_create_response_schema
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def create(conn, %{"acl_id" => acl_id, "member" => member_params}) do
|
||||
chosen =
|
||||
cond do
|
||||
@@ -160,8 +162,7 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{
|
||||
error:
|
||||
"#{String.capitalize(type)} members cannot have an admin or manager role"
|
||||
error: "#{String.capitalize(type)} members cannot have an admin or manager role"
|
||||
})
|
||||
else
|
||||
info_fetcher =
|
||||
@@ -191,7 +192,7 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
|> json(%{error: "Creation failed: #{inspect(error)}"})
|
||||
end
|
||||
else
|
||||
{:error, error} ->
|
||||
error ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: "Entity lookup failed: #{inspect(error)}"})
|
||||
@@ -206,7 +207,7 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
Updates the role of an ACL member.
|
||||
"""
|
||||
@spec update_role(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :update_role,
|
||||
operation(:update_role,
|
||||
summary: "Update ACL Member Role",
|
||||
description: "Updates the role of an ACL member identified by ACL ID and member external ID.",
|
||||
parameters: [
|
||||
@@ -235,6 +236,8 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
@acl_member_update_response_schema
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def update_role(conn, %{
|
||||
"acl_id" => acl_id,
|
||||
"member_id" => external_id,
|
||||
@@ -301,7 +304,7 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
Deletes an ACL member.
|
||||
"""
|
||||
@spec delete(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :delete,
|
||||
operation(:delete,
|
||||
summary: "Delete ACL Member",
|
||||
description: "Deletes an ACL member identified by ACL ID and member external ID.",
|
||||
parameters: [
|
||||
@@ -325,6 +328,8 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
@acl_member_delete_response_schema
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def delete(conn, %{"acl_id" => acl_id, "member_id" => external_id}) do
|
||||
external_id_str = to_string(external_id)
|
||||
|
||||
|
||||
@@ -32,16 +32,18 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
)
|
||||
end
|
||||
|
||||
def handle_server_event(%{event: :untrack_character, payload: character_id}, %{
|
||||
assigns: %{
|
||||
map_id: map_id
|
||||
}
|
||||
} = socket) do
|
||||
def handle_server_event(
|
||||
%{event: :untrack_character, payload: character_id},
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
:ok = WandererApp.Character.TrackingUtils.untrack([%{id: character_id}], map_id, self())
|
||||
socket
|
||||
end
|
||||
|
||||
|
||||
def handle_server_event(
|
||||
%{event: :characters_updated},
|
||||
%{
|
||||
@@ -276,6 +278,24 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"startTracking",
|
||||
%{"character_eve_id" => character_eve_id},
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id,
|
||||
current_user: %{id: current_user_id}
|
||||
}
|
||||
} = socket
|
||||
)
|
||||
when not is_nil(character_eve_id) do
|
||||
{:ok, character} = WandererApp.Character.get_by_eve_id("#{character_eve_id}")
|
||||
|
||||
WandererApp.Cache.delete("character:#{character.id}:tracking_paused")
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(event, body, socket),
|
||||
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
|
||||
|
||||
@@ -295,6 +315,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
|> Map.put(:alliance_ticker, Map.get(character, :alliance_ticker, ""))
|
||||
|> Map.put_new(:ship, WandererApp.Character.get_ship(character))
|
||||
|> Map.put_new(:location, get_location(character))
|
||||
|> Map.put_new(:tracking_paused, character |> Map.get(:tracking_paused, false))
|
||||
|
||||
defp get_location(character),
|
||||
do: %{
|
||||
|
||||
@@ -33,7 +33,8 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
"getCharactersTrackingInfo",
|
||||
"updateCharacterTracking",
|
||||
"updateFollowingCharacter",
|
||||
"updateMainCharacter"
|
||||
"updateMainCharacter",
|
||||
"startTracking"
|
||||
]
|
||||
|
||||
@map_system_events [
|
||||
|
||||
@@ -11,9 +11,7 @@ defmodule WandererAppWeb.ServerStatusLive do
|
||||
"server_status"
|
||||
)
|
||||
|
||||
{:ok, status} = WandererApp.Server.ServerStatusTracker.get_status()
|
||||
|
||||
{:ok, socket |> assign(server_online: not status.vip)}
|
||||
{:ok, socket |> assign(server_online: true)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
||||
Reference in New Issue
Block a user