Packages
game_server_sdk
1.0.1067
1.0.1082
1.0.1079
1.0.1078
1.0.1077
1.0.1076
1.0.1075
1.0.1074
1.0.1073
1.0.1070
1.0.1068
1.0.1067
1.0.1063
1.0.1059
1.0.1058
1.0.1057
1.0.1056
1.0.1055
1.0.1050
1.0.1049
1.0.1048
1.0.1047
1.0.1046
1.0.1044
1.0.1043
1.0.1042
1.0.1041
1.0.1040
1.0.1039
1.0.1038
1.0.1034
1.0.1033
1.0.1029
1.0.1028
1.0.1026
1.0.1025
1.0.1024
1.0.1023
1.0.1022
1.0.1021
1.0.1020
1.0.1019
1.0.1018
1.0.1017
1.0.1016
1.0.1015
1.0.1014
1.0.1013
1.0.1012
1.0.1011
1.0.1009
1.0.1008
1.0.1007
1.0.1006
1.0.1005
1.0.1004
1.0.1003
1.0.1001
1.0.999
1.0.998
1.0.997
1.0.996
1.0.995
1.0.994
1.0.993
1.0.992
1.0.991
1.0.990
1.0.989
1.0.988
1.0.987
1.0.986
1.0.985
1.0.984
1.0.983
1.0.982
1.0.981
1.0.980
1.0.979
1.0.978
1.0.977
1.0.976
1.0.975
1.0.974
1.0.973
1.0.972
1.0.971
1.0.970
1.0.969
1.0.968
1.0.967
1.0.966
1.0.965
1.0.964
1.0.963
1.0.962
1.0.961
1.0.959
1.0.958
1.0.956
1.0.951
1.0.950
1.0.943
1.0.942
1.0.941
1.0.940
1.0.938
1.0.936
1.0.935
1.0.931
1.0.929
1.0.928
1.0.927
1.0.926
1.0.925
1.0.924
1.0.923
1.0.921
1.0.920
1.0.919
1.0.918
1.0.917
1.0.916
1.0.911
1.0.910
1.0.902
1.0.899
1.0.898
1.0.897
1.0.896
1.0.894
1.0.893
1.0.891
1.0.890
1.0.889
1.0.888
1.0.887
1.0.886
1.0.885
1.0.884
1.0.883
1.0.882
1.0.881
1.0.880
1.0.879
1.0.878
1.0.877
1.0.26
1.0.25
1.0.22
1.0.21
1.0.20
1.0.19
1.0.15
1.0.14
1.0.13
1.0.12
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.1.0
SDK for GameServer hooks development. Provides type specs, documentation, and IDE autocomplete for GameServer modules without requiring the full server.
Current section
Files
Jump to
Current section
Files
lib/game_server/push.ex
defmodule GameServer.Push do
@moduledoc ~S"""
Push context – device push-token registry and (see `send_to_user/3`)
server-authoritative delivery of push notifications.
Devices register their FCM registration token or APNs device token against
the authenticated user; a user has many devices. Delivery routes **per
token** off the `provider` column (`"fcm"` | `"apns"`), falling back to the
zero-config `Log` provider when nothing is configured
(see `docs/specs/push.md`).
## Usage
# Register a device (typically via POST /me/push-tokens)
{:ok, token} = Push.register_token(user_id, %{
"token" => "fcm-registration-token",
"platform" => "android",
"device_id" => "stable-device-key"
})
# List a user's devices
tokens = Push.list_tokens(user_id, page: 1, page_size: 25)
# Remove one (DELETE /me/push-tokens/:id)
{:ok, _} = Push.delete_token(user_id, token.id)
**Note:** This is an SDK stub. Calling these functions will raise an error.
The actual implementation runs on the GameServer.
"""
@type user_id() :: Ecto.UUID.t()
@doc ~S"""
Remove any token row by id (admin). Returns `{:ok, %PushToken{}}` or
`{:error, :not_found}`.
"""
@spec admin_delete_token(Ecto.UUID.t()) :: {:ok, GameServer.Push.PushToken.t()} | {:error, :not_found}
def admin_delete_token(_id) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
{:ok, %GameServer.Push.PushToken{id: "", user_id: "", token: "", platform: "android", provider: "fcm", device_id: nil, disabled_at: nil, last_used_at: nil, metadata: %{}, inserted_at: ~U[1970-01-01 00:00:00Z], updated_at: ~U[1970-01-01 00:00:00Z]}}
_ ->
raise "GameServer.Push.admin_delete_token/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Count for `list_all_tokens/2` (same filters).
"""
@spec count_all_tokens(map()) :: non_neg_integer()
def count_all_tokens(_filters) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
0
_ ->
raise "GameServer.Push.count_all_tokens/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Count a user's registered tokens (including disabled).
"""
@spec count_tokens(user_id()) :: non_neg_integer()
def count_tokens(_user_id) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
0
_ ->
raise "GameServer.Push.count_tokens/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Remove a token row by id, scoped to `user_id` (the `DELETE /me/push-tokens/:id`
path). Returns `{:ok, %PushToken{}}` or `{:error, :not_found}`.
"""
@spec delete_token(user_id(), Ecto.UUID.t()) ::
{:ok, GameServer.Push.PushToken.t()} | {:error, :not_found}
def delete_token(_user_id, _id) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
{:ok, %GameServer.Push.PushToken{id: "", user_id: "", token: "", platform: "android", provider: "fcm", device_id: nil, disabled_at: nil, last_used_at: nil, metadata: %{}, inserted_at: ~U[1970-01-01 00:00:00Z], updated_at: ~U[1970-01-01 00:00:00Z]}}
_ ->
raise "GameServer.Push.delete_token/2 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Soft-disable a token the provider reported dead. The row is kept —
re-registration re-enables it — so a token bouncing between valid and
invalid never loses its device association.
"""
@spec disable_token(String.t()) :: :ok
def disable_token(_token) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
:ok
_ ->
raise "GameServer.Push.disable_token/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Whether every delivery is forced to the Log provider (`PUSH_ADAPTER=log`).
"""
@spec force_log?() :: boolean()
def force_log?() do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
false
_ ->
raise "GameServer.Push.force_log?/0 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Admin listing across all users. Supported `filters` keys (atom or string):
`:user_id`, `:platform`, `:provider`, and `:status` (`"live"` | `"disabled"`).
Supports pagination via `:page` and `:page_size` options; preloads `:user`
so the admin UI can show names, not UUIDs.
"""
@spec list_all_tokens(
map(),
keyword()
) :: [GameServer.Push.PushToken.t()]
def list_all_tokens(_filters, _opts) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
[]
_ ->
raise "GameServer.Push.list_all_tokens/2 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
List a user's registered tokens, newest first. Includes disabled rows (they
are the user's devices; clients can show them greyed out).
Supports pagination via `:page` and `:page_size` options.
"""
@spec list_tokens(
user_id(),
keyword()
) :: [GameServer.Push.PushToken.t()]
def list_tokens(_user_id, _opts) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
[]
_ ->
raise "GameServer.Push.list_tokens/2 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
The user's live (non-disabled) tokens — the delivery fan-out set. Unpaginated
by design: bounded by `max_push_tokens_per_user`.
"""
@spec live_tokens(user_id()) :: [GameServer.Push.PushToken.t()]
def live_tokens(_user_id) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
[]
_ ->
raise "GameServer.Push.live_tokens/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Bump `last_used_at` after a successful delivery.
"""
@spec mark_token_used(String.t()) :: :ok
def mark_token_used(_token) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
:ok
_ ->
raise "GameServer.Push.mark_token_used/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Resolve the delivery provider for a token: its `provider` column's module
when that module's `configured?/0` says it can deliver, else the zero-config
`Log` provider. `PUSH_ADAPTER=log` short-circuits everything to `Log`.
"""
@spec provider_for(GameServer.Push.PushToken.t()) :: module()
def provider_for(_push_token) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
nil
_ ->
raise "GameServer.Push.provider_for/1 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Register (or refresh) a device push token for `user_id`.
Upsert semantics, serialized under the `:push_tokens` advisory lock:
- a row with the same `token` already exists → it is claimed for this
user/device (a device that logged into another account must not keep
receiving the old account's pushes) and re-enabled;
- else a row with the same `(user_id, device_id)` exists → its token is
rotated in place and the row re-enabled;
- else a new row is inserted, subject to `max_push_tokens_per_user`
(counting live tokens only).
`provider` defaults from the platform when omitted: `"ios"` → `"apns"`,
anything else → `"fcm"`.
Returns `{:ok, %PushToken{}}`, `{:error, :too_many_tokens}`, or
`{:error, changeset}`.
"""
@spec register_token(user_id(), map()) ::
{:ok, GameServer.Push.PushToken.t()} | {:error, :too_many_tokens | Ecto.Changeset.t()}
def register_token(_user_id, _attrs) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
{:ok, %GameServer.Push.PushToken{id: "", user_id: "", token: "", platform: "android", provider: "fcm", device_id: nil, disabled_at: nil, last_used_at: nil, metadata: %{}, inserted_at: ~U[1970-01-01 00:00:00Z], updated_at: ~U[1970-01-01 00:00:00Z]}}
_ ->
raise "GameServer.Push.register_token/2 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Queue a push message to all of `user_id`'s live devices.
Server-authoritative: exposed to plugins through the SDK and to admins —
never as a public client endpoint. Best-effort by design: no live devices
means no jobs, and delivery failures never propagate back to the caller.
Returns `:ok` or `{:error, errors}` when the message fails validation
(see `GameServer.Push.Message.new/1`).
"""
@spec send_to_user(user_id(), map(), keyword()) :: :ok | {:error, map()}
def send_to_user(_user_id, _message_attrs, _opts) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
:ok
_ ->
raise "GameServer.Push.send_to_user/3 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Queue a push message to every live device of `user_ids`.
Small audiences enqueue delivery jobs inline; past 100
recipients the expansion itself becomes a `FanoutWorker` job (chunked,
restart-safe, deduped against identical double-broadcasts).
"""
@spec send_to_users([user_id()], map(), keyword()) :: :ok | {:error, map() | term()}
def send_to_users(_user_ids, _message_attrs, _opts) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
:ok
_ ->
raise "GameServer.Push.send_to_users/3 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Aggregate token counts for the admin stat card and runtime introspection:
`%{total: n, live: n, disabled: n, by_platform: %{...}, by_provider: %{...}}`
(platform/provider maps count live tokens only).
"""
@spec token_stats() :: map()
def token_stats() do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
%{}
_ ->
raise "GameServer.Push.token_stats/0 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Remove a token row by its raw `token` value, scoped to `user_id`.
Returns `{:ok, %PushToken{}}` or `{:error, :not_found}`.
"""
@spec unregister_token(user_id(), String.t()) ::
{:ok, GameServer.Push.PushToken.t()} | {:error, :not_found}
def unregister_token(_user_id, _token) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
{:ok, %GameServer.Push.PushToken{id: "", user_id: "", token: "", platform: "android", provider: "fcm", device_id: nil, disabled_at: nil, last_used_at: nil, metadata: %{}, inserted_at: ~U[1970-01-01 00:00:00Z], updated_at: ~U[1970-01-01 00:00:00Z]}}
_ ->
raise "GameServer.Push.unregister_token/2 is a stub - only available at runtime on GameServer"
end
end
@doc ~S"""
Whether the user has any live device. Cached (60s TTL + version bump on
register/remove/disable): `Notifications` asks this on every insert, and the
common no-device answer must not cost a query.
"""
@spec user_has_live_tokens?(user_id()) :: boolean()
def user_has_live_tokens?(_user_id) do
case Application.get_env(:game_server_sdk, :stub_mode, :raise) do
:placeholder ->
false
_ ->
raise "GameServer.Push.user_has_live_tokens?/1 is a stub - only available at runtime on GameServer"
end
end
end