Services
The services of Identica's API, and how to find those of features.
Every service is reached through the IdenticaAPI you got as described in Getting started.
A first service
import me.whereareiam.identica.IdenticaAPI;
public final class ExampleAddon {
private final IdenticaAPI identica;
public ExampleAddon(IdenticaAPI identica) {
this.identica = identica;
}
public void inspectProviders() {
identica.getProviderManager().getProviders().forEach(provider -> {
if (provider != null && provider.getDescriptor() != null) {
System.out.println(provider.getDescriptor().getId());
}
});
}
}Finding other services
Services without a getter on IdenticaAPI, such as the API of a feature, are found by type. findService returns an empty Optional when Identica offers no service of that type.
Optional<FeatureRegistry> features = identica.findService(FeatureRegistry.class);Here FeatureRegistry is me.whereareiam.identica.feature.FeatureRegistry.
Feature services and provider policy
Feature implementations are compiled into Identica. Find their APIs by type, and check effective provider enablement before running optional integrations.
FeatureRegistry features = identica.findService(FeatureRegistry.class).orElseThrow();
if (features.isEnabled(providerId, "verification")) {
identica.findService(VerificationService.class).ifPresent(verification -> {
// Resolve verification for this provider.
});
}isEnabled combines provider-declared support with global defaults and provider overrides. Provider guarantees are inspected separately through ProviderDescriptor.hasGuarantee(ProviderGuarantee.OWNED_USERNAME); no feature switch grants or removes a guarantee.
See Provider guarantees and features for the contracts.
Common services worth using
These are some of the most useful services on IdenticaAPI for addon plugins:
getProviderManager()for listing and inspecting loaded providersgetProviderOperations()for provider eligibility and runtime provider operationsgetSessionService()for finding, listing, opening, or closing sessionsgetAccountService()for account lookup and lifecycle operationsgetIdentityService()for the identities of the players on this proxygetMessageService()for telling players Identica's messages and showing them in more ways than chat, see Messages to playersgetReplicationSystem()if you intentionally integrate with Identica's replicated caches or channelsgetEventManager()for registering or calling Identica eventsgetPendingScenarioService()for whether a player still has to authenticate, register or migrategetConfigCatalog()for the loaded configuration documents, such as every messages documentgetReloadables()for being reloaded by/identica admin reload
Addons that hold back chat or commands until a player has authenticated are covered in Writing an addon.
When verification is selected, find VerificationService for enrollment, challenge, selection, and reset flows, or VerificationRegistry for method discovery, through identica.findService(...). Both types belong to me.whereareiam.identica.feature.verification.
A provider's verification step is a StatefulStep. /2fa confirm hands the typed code to the pending journey as a VerificationCodeAttempt in the run's state; the step takes it out of the state and passes it with VerificationResolutionRequest.code(...). resolveVerification submits the code before it resolves the requirement. A code the challenge does not take ends as REJECTED, with the reason in getRejection(): INVALID for a wrong code. The step keeps waiting with the confirm command's invalid-code message for a wrong code and its no-pending message otherwise.
Sessions
import me.whereareiam.identica.IdenticaAPI;
import me.whereareiam.identica.identity.session.SessionService;
import java.util.UUID;
public final class ExampleAddon {
private final IdenticaAPI identica;
public ExampleAddon(IdenticaAPI identica) {
this.identica = identica;
}
public void printSession(UUID uniqueId) {
SessionService sessionService = identica.getSessionService();
sessionService.findByUniqueId(uniqueId).thenAccept(optionalSession -> {
optionalSession.ifPresent(session ->
System.out.println(session.getProviderId() + " -> " + session.getEffectiveUsername())
);
});
}
}A session belongs to a connection: session.getConnection() returns the SessionConnection with the connection's unique id and the replication.serverId of the proxy that holds it. An account has one session, held by one connection:
findByUniqueId(uniqueId)returns the account's session, enough to learn whether the account is online or how it logged infindByConnection(uniqueId, SessionConnection.of(player.getUniqueId()))returns it only when that connection holds it, which a connection whose session was replaced no longer doeslist(page, pageSize)pages through session ids, whichfindBySessionIdresolves
SessionService.open(session) continues a stored session only when the new session names the same connection; any other connection is a concurrent login decided by the configured concurrency policy. The policy decides on the session the account had when the call read it: when another login of the account, typically on another proxy, opened a session in between, the returned future fails with ConcurrentSessionChangeException and nothing of the call is kept, so open the session again once you have told the player. close(uniqueId) ends the account's session. To end it only when one connection holds it, for example when that player leaves, set the connection on the close request; a session another connection holds is then left alone:
sessionService.close(SessionCloseRequest.builder()
.uniqueId(accountUniqueId)
.connection(SessionConnection.of(player.getUniqueId()))
.build());