diff --git a/.gitignore b/.gitignore
index b585321..114d915 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,14 @@
+.agents/
+.codex/
.vs/
bin/
+Binaries
+Intermediate/
obj/
.DS_Store
.env
*.filters
*.user
+*.lscache
desktop.ini
diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES
index e13d875..7cb3a8d 100644
--- a/.openapi-generator/FILES
+++ b/.openapi-generator/FILES
@@ -1,60 +1,67 @@
-src/Tribufu.Generated/Api/TribufuGeneratedApi.cs
-src/Tribufu.Generated/Client/ApiClient.cs
-src/Tribufu.Generated/Client/ApiException.cs
-src/Tribufu.Generated/Client/ApiResponse.cs
-src/Tribufu.Generated/Client/ClientUtils.cs
-src/Tribufu.Generated/Client/Configuration.cs
-src/Tribufu.Generated/Client/ExceptionFactory.cs
-src/Tribufu.Generated/Client/GlobalConfiguration.cs
-src/Tribufu.Generated/Client/HttpMethod.cs
-src/Tribufu.Generated/Client/IApiAccessor.cs
-src/Tribufu.Generated/Client/IAsynchronousClient.cs
-src/Tribufu.Generated/Client/IReadableConfiguration.cs
-src/Tribufu.Generated/Client/ISynchronousClient.cs
-src/Tribufu.Generated/Client/Multimap.cs
-src/Tribufu.Generated/Client/OpenAPIDateConverter.cs
-src/Tribufu.Generated/Client/RequestOptions.cs
-src/Tribufu.Generated/Client/RetryConfiguration.cs
-src/Tribufu.Generated/Model/AbstractOpenAPISchema.cs
-src/Tribufu.Generated/Model/Account.cs
-src/Tribufu.Generated/Model/Application.cs
-src/Tribufu.Generated/Model/ApplicationType.cs
-src/Tribufu.Generated/Model/AuthorizeRequest.cs
-src/Tribufu.Generated/Model/CodeChallengeMethod.cs
-src/Tribufu.Generated/Model/CryptoViewModel.cs
-src/Tribufu.Generated/Model/Game.cs
-src/Tribufu.Generated/Model/GameServer.cs
-src/Tribufu.Generated/Model/GameServerCluster.cs
-src/Tribufu.Generated/Model/GrantType.cs
-src/Tribufu.Generated/Model/Group.cs
-src/Tribufu.Generated/Model/GroupGame.cs
-src/Tribufu.Generated/Model/GroupMember.cs
-src/Tribufu.Generated/Model/GroupRank.cs
-src/Tribufu.Generated/Model/HashViewModel.cs
-src/Tribufu.Generated/Model/IntrospectRequest.cs
-src/Tribufu.Generated/Model/IpAddress.cs
-src/Tribufu.Generated/Model/LeaderboardItem.cs
-src/Tribufu.Generated/Model/LeaderboardOrder.cs
-src/Tribufu.Generated/Model/LoginProvider.cs
-src/Tribufu.Generated/Model/LoginRequest.cs
-src/Tribufu.Generated/Model/LoginResponse.cs
-src/Tribufu.Generated/Model/Package.cs
-src/Tribufu.Generated/Model/Profile.cs
-src/Tribufu.Generated/Model/ProfileGame.cs
-src/Tribufu.Generated/Model/ProfileGroup.cs
-src/Tribufu.Generated/Model/RefreshRequest.cs
-src/Tribufu.Generated/Model/RegisterRequest.cs
-src/Tribufu.Generated/Model/ResponseType.cs
-src/Tribufu.Generated/Model/RevokeRequest.cs
-src/Tribufu.Generated/Model/SearchRequest.cs
-src/Tribufu.Generated/Model/SearchType.cs
-src/Tribufu.Generated/Model/ServerMetrics.cs
-src/Tribufu.Generated/Model/ServerStatus.cs
-src/Tribufu.Generated/Model/Subscription.cs
-src/Tribufu.Generated/Model/TokenHintType.cs
-src/Tribufu.Generated/Model/TokenRequest.cs
-src/Tribufu.Generated/Model/TokenResponse.cs
-src/Tribufu.Generated/Model/TokenType.cs
-src/Tribufu.Generated/Model/UpdateProfile.cs
-src/Tribufu.Generated/Model/UserInfo.cs
-src/Tribufu.Generated/Model/UserType.cs
+src/Tribufu/Api/TribufuGeneratedApi.cs
+src/Tribufu/Client/ApiClient.cs
+src/Tribufu/Client/ApiException.cs
+src/Tribufu/Client/ApiResponse.cs
+src/Tribufu/Client/ClientUtils.cs
+src/Tribufu/Client/Configuration.cs
+src/Tribufu/Client/ExceptionFactory.cs
+src/Tribufu/Client/GlobalConfiguration.cs
+src/Tribufu/Client/HttpMethod.cs
+src/Tribufu/Client/IApiAccessor.cs
+src/Tribufu/Client/IAsynchronousClient.cs
+src/Tribufu/Client/IReadableConfiguration.cs
+src/Tribufu/Client/ISynchronousClient.cs
+src/Tribufu/Client/Multimap.cs
+src/Tribufu/Client/OpenAPIDateConverter.cs
+src/Tribufu/Client/RequestOptions.cs
+src/Tribufu/Client/RetryConfiguration.cs
+src/Tribufu/Model/AbstractOpenAPISchema.cs
+src/Tribufu/Model/Application.cs
+src/Tribufu/Model/ApplicationType.cs
+src/Tribufu/Model/AuthorizeRequest.cs
+src/Tribufu/Model/ClientInfo.cs
+src/Tribufu/Model/ClientType.cs
+src/Tribufu/Model/CodeChallengeMethod.cs
+src/Tribufu/Model/CodeResponse.cs
+src/Tribufu/Model/CreateUser.cs
+src/Tribufu/Model/CryptoViewModel.cs
+src/Tribufu/Model/ExternalAccount.cs
+src/Tribufu/Model/Game.cs
+src/Tribufu/Model/GameServer.cs
+src/Tribufu/Model/GameServerCluster.cs
+src/Tribufu/Model/GameServerQuery.cs
+src/Tribufu/Model/GameServerStatus.cs
+src/Tribufu/Model/GrantType.cs
+src/Tribufu/Model/Group.cs
+src/Tribufu/Model/GroupApplication.cs
+src/Tribufu/Model/GroupMember.cs
+src/Tribufu/Model/GroupRank.cs
+src/Tribufu/Model/HashViewModel.cs
+src/Tribufu/Model/IdentityProviderType.cs
+src/Tribufu/Model/IntrospectRequest.cs
+src/Tribufu/Model/IntrospectResponse.cs
+src/Tribufu/Model/IpAddress.cs
+src/Tribufu/Model/LeaderboardItem.cs
+src/Tribufu/Model/LeaderboardOrder.cs
+src/Tribufu/Model/ModelClient.cs
+src/Tribufu/Model/Package.cs
+src/Tribufu/Model/PackageRelease.cs
+src/Tribufu/Model/Product.cs
+src/Tribufu/Model/ProductPrice.cs
+src/Tribufu/Model/ProductType.cs
+src/Tribufu/Model/Profile.cs
+src/Tribufu/Model/ProfileGame.cs
+src/Tribufu/Model/ProfileGroup.cs
+src/Tribufu/Model/ResponseType.cs
+src/Tribufu/Model/RevokeRequest.cs
+src/Tribufu/Model/Search.cs
+src/Tribufu/Model/SearchType.cs
+src/Tribufu/Model/ServerMetrics.cs
+src/Tribufu/Model/StorageFile.cs
+src/Tribufu/Model/TokenHintType.cs
+src/Tribufu/Model/TokenRequest.cs
+src/Tribufu/Model/TokenResponse.cs
+src/Tribufu/Model/TokenType.cs
+src/Tribufu/Model/UpdateProfile.cs
+src/Tribufu/Model/UserInfo.cs
+src/Tribufu/Model/UserType.cs
diff --git a/Directory.Build.props b/Directory.Build.props
index 32e4972..5e893d6 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,9 +5,17 @@
Copyright (c) Tribufu. All Rights Reserved.
MIT
git
- https://github.com/tribufu/sdk-dotnet
- 1.1.0
+ https://github.com/Tribufu/TribufuDotNet
+ 1.3.0
$(Version).0
$(NoWarn);0618;1591;1998;2002;8767
+
+ $(SolutionDir)Intermediate\DotNet\$(MSBuildProjectName)\
+ $(BaseIntermediateOutputPath)$(Configuration)\
+ $(BaseIntermediateOutputPath)
+ $(IntermediateOutputPath)
+ $(SolutionDir)Binaries\$(Configuration)\DotNet\
+ $(SolutionDir)Intermediate\
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index e95a987..0640a39 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,11 +3,22 @@
true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index e684056..a5bf230 100644
--- a/README.md
+++ b/README.md
@@ -1,279 +1,23 @@
-# Tribufu - the C# library for the Tribufu API
+# Tribufu .NET
-REST API to access Tribufu services.
+.NET SDK to access Tribufu APIs and services.
-This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
+[![nuget][nuget-badge]][nuget-url]
+[![MIT License][mit-badge]][mit-url]
+[![Discord Chat][discord-badge]][discord-url]
-- API version: 1.1.0
-- SDK version: 1.0.0
-- Generator version: 7.8.0
-- Build package: org.openapitools.codegen.languages.CSharpClientCodegen
- For more information, please visit [https://www.tribufu.com/contact](https://www.tribufu.com/contact)
+[nuget-badge]: https://img.shields.io/nuget/v/Tribufu.svg
+[nuget-url]: https://www.nuget.org/packages/Tribufu
+[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
+[mit-url]: https://github.com/Tribufu/TribufuDotNet/blob/main/LICENSE.txt
+[discord-badge]: https://img.shields.io/discord/276504514616623104.svg?logo=discord&style=flat-square
+[discord-url]: https://www.tribufu.com/discord
-
-## Frameworks supported
+[Website](https://www.tribufu.com) |
+[Discord](https://www.tribufu.com/discord)
-
-## Dependencies
+## License
-- [Json.NET](https://www.nuget.org/packages/Newtonsoft.Json/) - 13.0.2 or later
-- [JsonSubTypes](https://www.nuget.org/packages/JsonSubTypes/) - 1.8.0 or later
-- [System.ComponentModel.Annotations](https://www.nuget.org/packages/System.ComponentModel.Annotations) - 5.0.0 or later
-
-The DLLs included in the package may not be the latest version. We recommend using [NuGet](https://docs.nuget.org/consume/installing-nuget) to obtain the latest version of the packages:
-```
-Install-Package Newtonsoft.Json
-Install-Package JsonSubTypes
-Install-Package System.ComponentModel.Annotations
-```
-
-## Installation
-Run the following command to generate the DLL
-- [Mac/Linux] `/bin/sh build.sh`
-- [Windows] `build.bat`
-
-Then include the DLL (under the `bin` folder) in the C# project, and use the namespaces:
-```csharp
-using Tribufu.Api;
-using Tribufu.Client;
-using Tribufu.Model;
-```
-
-## Packaging
-
-A `.nuspec` is included with the project. You can follow the Nuget quickstart to [create](https://docs.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package#create-the-package) and [publish](https://docs.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package#publish-the-package) packages.
-
-This `.nuspec` uses placeholders from the `.csproj`, so build the `.csproj` directly:
-
-```
-nuget pack -Build -OutputDirectory out Tribufu.csproj
-```
-
-Then, publish to a [local feed](https://docs.microsoft.com/en-us/nuget/hosting-packages/local-feeds) or [other host](https://docs.microsoft.com/en-us/nuget/hosting-packages/overview) and consume the new package via Nuget as usual.
-
-
-## Usage
-
-To use the API client with a HTTP proxy, setup a `System.Net.WebProxy`
-```csharp
-Configuration c = new Configuration();
-System.Net.WebProxy webProxy = new System.Net.WebProxy("http://myProxyUrl:80/");
-webProxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
-c.Proxy = webProxy;
-```
-
-### Connections
-Each ApiClass (properly the ApiClient inside it) will create an instance of HttpClient. It will use that for the entire lifecycle and dispose it when called the Dispose method.
-
-To better manager the connections it's a common practice to reuse the HttpClient and HttpClientHandler (see [here](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net) for details). To use your own HttpClient instance just pass it to the ApiClass constructor.
-
-```csharp
-HttpClientHandler yourHandler = new HttpClientHandler();
-HttpClient yourHttpClient = new HttpClient(yourHandler);
-var api = new YourApiClass(yourHttpClient, yourHandler);
-```
-
-If you want to use an HttpClient and don't have access to the handler, for example in a DI context in Asp.net Core when using IHttpClientFactory.
-
-```csharp
-HttpClient yourHttpClient = new HttpClient();
-var api = new YourApiClass(yourHttpClient);
-```
-You'll loose some configuration settings, the features affected are: Setting and Retrieving Cookies, Client Certificates, Proxy settings. You need to either manually handle those in your setup of the HttpClient or they won't be available.
-
-Here an example of DI setup in a sample web project:
-
-```csharp
-services.AddHttpClient(httpClient =>
- new PetApi(httpClient));
-```
-
-
-
-## Getting Started
-
-```csharp
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Net.Http;
-using Tribufu.Api;
-using Tribufu.Client;
-using Tribufu.Model;
-
-namespace Example
-{
- public class Example
- {
- public static void Main()
- {
-
- Configuration config = new Configuration();
- config.BasePath = "http://localhost";
- // Configure API key authorization: ApiKey
- config.ApiKey.Add("Authorization", "YOUR_API_KEY");
- // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
- // config.ApiKeyPrefix.Add("Authorization", "Bearer");
-
- // create instances of HttpClient, HttpClientHandler to be reused later with different Api classes
- HttpClient httpClient = new HttpClient();
- HttpClientHandler httpClientHandler = new HttpClientHandler();
- var apiInstance = new TribufuApi(httpClient, config, httpClientHandler);
- var authorizeRequest = new AuthorizeRequest?(); // AuthorizeRequest? | (optional)
-
- try
- {
- // Authorize the client to access the user information.
- apiInstance.Authorize(authorizeRequest);
- }
- catch (ApiException e)
- {
- Debug.Print("Exception when calling TribufuApi.Authorize: " + e.Message );
- Debug.Print("Status Code: "+ e.ErrorCode);
- Debug.Print(e.StackTrace);
- }
-
- }
- }
-}
-```
-
-
-## Documentation for API Endpoints
-
-All URIs are relative to *http://localhost*
-
-Class | Method | HTTP request | Description
------------- | ------------- | ------------- | -------------
-*TribufuApi* | [**Authorize**](docs/TribufuApi.md#authorize) | **POST** /v1/oauth2/authorize | Authorize the client to access the user information.
-*TribufuApi* | [**ChangeEmail**](docs/TribufuApi.md#changeemail) | **PUT** /v1/users/{id}/email | Change the email of a user.
-*TribufuApi* | [**ChangePassword**](docs/TribufuApi.md#changepassword) | **PUT** /v1/users/{id}/password | Change the password of a user.
-*TribufuApi* | [**ClaimGameServer**](docs/TribufuApi.md#claimgameserver) | **PUT** /v1/servers/{id}/claim | Claim a game server.
-*TribufuApi* | [**ConvertBase64**](docs/TribufuApi.md#convertbase64) | **POST** /v1/utils/base64 | Convert a string to base64 or vice versa.
-*TribufuApi* | [**CreateGameServer**](docs/TribufuApi.md#creategameserver) | **POST** /v1/servers | Create a new game server.
-*TribufuApi* | [**CreateGameServerCluster**](docs/TribufuApi.md#creategameservercluster) | **POST** /v1/clusters | Create a new game server cluster.
-*TribufuApi* | [**CreateGroup**](docs/TribufuApi.md#creategroup) | **POST** /v1/groups | Create a new group.
-*TribufuApi* | [**CreateToken**](docs/TribufuApi.md#createtoken) | **POST** /v1/oauth2/token | Create a new token with grant type.
-*TribufuApi* | [**DeleteGameServer**](docs/TribufuApi.md#deletegameserver) | **DELETE** /v1/servers/{id} | Delete a game server.
-*TribufuApi* | [**DeleteGameServerCluster**](docs/TribufuApi.md#deletegameservercluster) | **DELETE** /v1/clusters/{id} | Delete a game server cluster.
-*TribufuApi* | [**DeleteGroup**](docs/TribufuApi.md#deletegroup) | **DELETE** /v1/groups/{id} | Delete a group.
-*TribufuApi* | [**GenerateFlakeId**](docs/TribufuApi.md#generateflakeid) | **GET** /v1/utils/flake | Generate one or more flake ids.
-*TribufuApi* | [**GenerateFlakeIdFromTimestamp**](docs/TribufuApi.md#generateflakeidfromtimestamp) | **GET** /v1/utils/flake/{timestamp} | Generate one or more flake ids from a timestamp.
-*TribufuApi* | [**GeneratePassword**](docs/TribufuApi.md#generatepassword) | **GET** /v1/utils/password | Generate a random password.
-*TribufuApi* | [**GenerateUuid**](docs/TribufuApi.md#generateuuid) | **GET** /v1/utils/uuid | Generate one or more uuids with a specific version.
-*TribufuApi* | [**GetClientInfo**](docs/TribufuApi.md#getclientinfo) | **GET** /v1/oauth2/clientinfo | Get current client information.
-*TribufuApi* | [**GetCurrentIpAddress**](docs/TribufuApi.md#getcurrentipaddress) | **GET** /v1/geoip | Get current ip address location.
-*TribufuApi* | [**GetGameById**](docs/TribufuApi.md#getgamebyid) | **GET** /v1/games/{id} | Get a game by id.
-*TribufuApi* | [**GetGameClustersByGameId**](docs/TribufuApi.md#getgameclustersbygameid) | **GET** /v1/games/{id}/clusters | Get a list of game server clusters of a game.
-*TribufuApi* | [**GetGameItems**](docs/TribufuApi.md#getgameitems) | **GET** /v1/games/{id}/items | Get a list of game items.
-*TribufuApi* | [**GetGameServerByAddressAndQueryPort**](docs/TribufuApi.md#getgameserverbyaddressandqueryport) | **GET** /v1/servers/address/{address}:{port} | Get a game server by address and query port.
-*TribufuApi* | [**GetGameServerById**](docs/TribufuApi.md#getgameserverbyid) | **GET** /v1/servers/{id} | Get a game server by id.
-*TribufuApi* | [**GetGameServerClusterById**](docs/TribufuApi.md#getgameserverclusterbyid) | **GET** /v1/clusters/{id} | Get a game server cluster by id.
-*TribufuApi* | [**GetGameServerClusters**](docs/TribufuApi.md#getgameserverclusters) | **GET** /v1/clusters | Get a list of game server clusters.
-*TribufuApi* | [**GetGameServers**](docs/TribufuApi.md#getgameservers) | **GET** /v1/servers | Get a list of game servers.
-*TribufuApi* | [**GetGameServersByCountry**](docs/TribufuApi.md#getgameserversbycountry) | **GET** /v1/servers/country/{country} | Get a list of game servers from a country.
-*TribufuApi* | [**GetGameServersByGameId**](docs/TribufuApi.md#getgameserversbygameid) | **GET** /v1/games/{id}/servers | Get a list of game servers of a game.
-*TribufuApi* | [**GetGameServersCountries**](docs/TribufuApi.md#getgameserverscountries) | **GET** /v1/servers/countries | Get a list of countries with the number of game servers.
-*TribufuApi* | [**GetGameServersMetrics**](docs/TribufuApi.md#getgameserversmetrics) | **GET** /v1/servers/metrics | Get metrics about the tracked game servers.
-*TribufuApi* | [**GetGames**](docs/TribufuApi.md#getgames) | **GET** /v1/games | Get a list of games.
-*TribufuApi* | [**GetGroupById**](docs/TribufuApi.md#getgroupbyid) | **GET** /v1/groups/{id} | Get a group by id.
-*TribufuApi* | [**GetGroupByTag**](docs/TribufuApi.md#getgroupbytag) | **GET** /v1/groups/tag/{tag} | Get a group by tag.
-*TribufuApi* | [**GetGroupByUuid**](docs/TribufuApi.md#getgroupbyuuid) | **GET** /v1/groups/uuid/{uuid} | Get a group by uuid.
-*TribufuApi* | [**GetGroupGames**](docs/TribufuApi.md#getgroupgames) | **GET** /v1/groups/{id}/games | Get a list of games of a group.
-*TribufuApi* | [**GetGroupMembers**](docs/TribufuApi.md#getgroupmembers) | **GET** /v1/groups/{id}/members | Get a list of members in a group.
-*TribufuApi* | [**GetGroups**](docs/TribufuApi.md#getgroups) | **GET** /v1/groups | Get a list of groups.
-*TribufuApi* | [**GetIpAddress**](docs/TribufuApi.md#getipaddress) | **GET** /v1/geoip/addresses/{address} | Get a ip address location.
-*TribufuApi* | [**GetIpAddresses**](docs/TribufuApi.md#getipaddresses) | **GET** /v1/geoip/addresses | Get a list of ip addresses.
-*TribufuApi* | [**GetLeaderboard**](docs/TribufuApi.md#getleaderboard) | **GET** /v1/leaderboard | Get the top 20 leaderboard users.
-*TribufuApi* | [**GetMe**](docs/TribufuApi.md#getme) | **GET** /v1/me | Get current user information.
-*TribufuApi* | [**GetPackageById**](docs/TribufuApi.md#getpackagebyid) | **GET** /v1/packages/{id} | Get a package by id.
-*TribufuApi* | [**GetPackages**](docs/TribufuApi.md#getpackages) | **GET** /v1/packages | Get a list of packages.
-*TribufuApi* | [**GetPublicKeys**](docs/TribufuApi.md#getpublickeys) | **GET** /v1/oauth2/jwks | Get the public keys for the client.
-*TribufuApi* | [**GetSubscriptionById**](docs/TribufuApi.md#getsubscriptionbyid) | **GET** /v1/subscriptions/{id} | Get a subscription by id.
-*TribufuApi* | [**GetSubscriptions**](docs/TribufuApi.md#getsubscriptions) | **GET** /v1/subscriptions | Get a list of subscriptions.
-*TribufuApi* | [**GetUserAccounts**](docs/TribufuApi.md#getuseraccounts) | **GET** /v1/users/{id}/accounts | Get a list of connected accounts of the user.
-*TribufuApi* | [**GetUserById**](docs/TribufuApi.md#getuserbyid) | **GET** /v1/users/{id} | Get a user profile by id.
-*TribufuApi* | [**GetUserByName**](docs/TribufuApi.md#getuserbyname) | **GET** /v1/users/name/{name} | Get a user profile by name.
-*TribufuApi* | [**GetUserByUuid**](docs/TribufuApi.md#getuserbyuuid) | **GET** /v1/users/uuid/{uuid} | Get a user profile by uuid.
-*TribufuApi* | [**GetUserFriends**](docs/TribufuApi.md#getuserfriends) | **GET** /v1/users/{id}/friends | Get a list of friends of the user.
-*TribufuApi* | [**GetUserGames**](docs/TribufuApi.md#getusergames) | **GET** /v1/users/{id}/games | Get a list of games the user has played.
-*TribufuApi* | [**GetUserGroups**](docs/TribufuApi.md#getusergroups) | **GET** /v1/users/{id}/groups | Get a list of groups the user is a member of.
-*TribufuApi* | [**GetUserInfo**](docs/TribufuApi.md#getuserinfo) | **GET** /v1/oauth2/userinfo | Get current user information.
-*TribufuApi* | [**GetUserPunishments**](docs/TribufuApi.md#getuserpunishments) | **GET** /v1/users/{id}/punishments | Get a list of punishments the user has received.
-*TribufuApi* | [**GetUserServers**](docs/TribufuApi.md#getuserservers) | **GET** /v1/users/{id}/servers | Get a list of servers the user is owner of.
-*TribufuApi* | [**GetUsers**](docs/TribufuApi.md#getusers) | **GET** /v1/users | Get a list of user profiles.
-*TribufuApi* | [**HashArgon2**](docs/TribufuApi.md#hashargon2) | **POST** /v1/utils/argon2 | Hash a string using argon2.
-*TribufuApi* | [**HashBcrypt**](docs/TribufuApi.md#hashbcrypt) | **POST** /v1/utils/bcrypt | Hash a string using bcrypt.
-*TribufuApi* | [**HashMd5**](docs/TribufuApi.md#hashmd5) | **POST** /v1/utils/md5 | Hash a string using md5.
-*TribufuApi* | [**HashSha256**](docs/TribufuApi.md#hashsha256) | **POST** /v1/utils/sha256 | Hash a string using sha256.
-*TribufuApi* | [**IntrospectToken**](docs/TribufuApi.md#introspecttoken) | **POST** /v1/oauth2/introspect | Introspect a token.
-*TribufuApi* | [**Login**](docs/TribufuApi.md#login) | **POST** /v1/login | Login with name or email and password.
-*TribufuApi* | [**Logout**](docs/TribufuApi.md#logout) | **POST** /v1/logout | Invalidate credentials.
-*TribufuApi* | [**Refresh**](docs/TribufuApi.md#refresh) | **POST** /v1/refresh | Refresh credentials.
-*TribufuApi* | [**Register**](docs/TribufuApi.md#register) | **POST** /v1/register | Create a new user.
-*TribufuApi* | [**RevokeToken**](docs/TribufuApi.md#revoketoken) | **POST** /v1/oauth2/revoke | Revoke a token.
-*TribufuApi* | [**Search**](docs/TribufuApi.md#search) | **POST** /v1/search | Advanced search for servers or players.
-*TribufuApi* | [**UpdateGameServer**](docs/TribufuApi.md#updategameserver) | **PUT** /v1/servers/{id} | Update a game server.
-*TribufuApi* | [**UpdateGameServerCluster**](docs/TribufuApi.md#updategameservercluster) | **PUT** /v1/clusters/{id} | Update a game server cluster.
-*TribufuApi* | [**UpdateGroup**](docs/TribufuApi.md#updategroup) | **PUT** /v1/groups/{id} | Update a group.
-*TribufuApi* | [**UpdateUserProfile**](docs/TribufuApi.md#updateuserprofile) | **PUT** /v1/users/{id}/profile | Update a user profile.
-
-
-
-## Documentation for Models
-
- - [Model.Account](docs/Account.md)
- - [Model.Application](docs/Application.md)
- - [Model.ApplicationType](docs/ApplicationType.md)
- - [Model.AuthorizeRequest](docs/AuthorizeRequest.md)
- - [Model.CodeChallengeMethod](docs/CodeChallengeMethod.md)
- - [Model.CryptoViewModel](docs/CryptoViewModel.md)
- - [Model.Game](docs/Game.md)
- - [Model.GameServer](docs/GameServer.md)
- - [Model.GameServerCluster](docs/GameServerCluster.md)
- - [Model.GrantType](docs/GrantType.md)
- - [Model.Group](docs/Group.md)
- - [Model.GroupGame](docs/GroupGame.md)
- - [Model.GroupMember](docs/GroupMember.md)
- - [Model.GroupRank](docs/GroupRank.md)
- - [Model.HashViewModel](docs/HashViewModel.md)
- - [Model.IntrospectRequest](docs/IntrospectRequest.md)
- - [Model.IpAddress](docs/IpAddress.md)
- - [Model.LeaderboardItem](docs/LeaderboardItem.md)
- - [Model.LeaderboardOrder](docs/LeaderboardOrder.md)
- - [Model.LoginProvider](docs/LoginProvider.md)
- - [Model.LoginRequest](docs/LoginRequest.md)
- - [Model.LoginResponse](docs/LoginResponse.md)
- - [Model.Package](docs/Package.md)
- - [Model.Profile](docs/Profile.md)
- - [Model.ProfileGame](docs/ProfileGame.md)
- - [Model.ProfileGroup](docs/ProfileGroup.md)
- - [Model.RefreshRequest](docs/RefreshRequest.md)
- - [Model.RegisterRequest](docs/RegisterRequest.md)
- - [Model.ResponseType](docs/ResponseType.md)
- - [Model.RevokeRequest](docs/RevokeRequest.md)
- - [Model.SearchRequest](docs/SearchRequest.md)
- - [Model.SearchType](docs/SearchType.md)
- - [Model.ServerMetrics](docs/ServerMetrics.md)
- - [Model.ServerStatus](docs/ServerStatus.md)
- - [Model.Subscription](docs/Subscription.md)
- - [Model.TokenHintType](docs/TokenHintType.md)
- - [Model.TokenRequest](docs/TokenRequest.md)
- - [Model.TokenResponse](docs/TokenResponse.md)
- - [Model.TokenType](docs/TokenType.md)
- - [Model.UpdateProfile](docs/UpdateProfile.md)
- - [Model.UserInfo](docs/UserInfo.md)
- - [Model.UserType](docs/UserType.md)
-
-
-
-## Documentation for Authorization
-
-
-Authentication schemes defined for the API:
-
-### ApiKey
-
-- **Type**: API key
-- **API key parameter name**: Authorization
-- **Location**: HTTP header
+This project is licensed under the [MIT License].
+[MIT License]: https://github.com/Tribufu/TribufuDotNet/blob/main/LICENSE.txt
diff --git a/scripts/generate.ps1 b/Scripts/Generate.ps1
similarity index 50%
rename from scripts/generate.ps1
rename to Scripts/Generate.ps1
index c2c8b9a..abb11fc 100644
--- a/scripts/generate.ps1
+++ b/Scripts/Generate.ps1
@@ -1,5 +1,7 @@
#!/usr/bin/env pwsh
+$ErrorActionPreference = "Stop"
+
$env:CSHARP_POST_PROCESS_FILE = ""
java -jar ./vendor/openapi-generator/openapi-generator-cli.jar generate `
@@ -7,6 +9,15 @@ java -jar ./vendor/openapi-generator/openapi-generator-cli.jar generate `
-g csharp `
-o . `
--global-property apis,models,supportingFiles,apiDocs=false,modelDocs=false,apiTests=false,modelTests=false `
- --additional-properties=packageName=Tribufu.Generated,library=restsharp,zeroBasedEnums=true,nullableReferenceTypes=true `
+ --additional-properties=packageName=Tribufu,library=restsharp,targetFramework=net47,zeroBasedEnums=true `
--openapi-normalizer SET_TAGS_FOR_ALL_OPERATIONS=TribufuGenerated `
--skip-validate-spec
+
+$srcDir = "./src"
+$sourceDir = "./Source"
+
+if (Test-Path $srcDir) {
+ New-Item -ItemType Directory -Force -Path $sourceDir | Out-Null
+ Copy-Item -Path (Join-Path $srcDir "*") -Destination $sourceDir -Recurse -Force
+ Remove-Item $srcDir -Recurse -Force
+}
diff --git a/Scripts/Package.ps1 b/Scripts/Package.ps1
new file mode 100644
index 0000000..658179e
--- /dev/null
+++ b/Scripts/Package.ps1
@@ -0,0 +1,7 @@
+#!/usr/bin/env pwsh
+
+Get-ChildItem ./Intermediate -Filter *.nupkg -Recurse -Force | Remove-Item -Force
+
+dotnet clean
+dotnet build -c Release
+dotnet pack -c Release -o ./Intermediate
diff --git a/Source/Tribufu.AspNetCore/Extensions/HttpContextExtensions.cs b/Source/Tribufu.AspNetCore/Extensions/HttpContextExtensions.cs
new file mode 100644
index 0000000..b65a12b
--- /dev/null
+++ b/Source/Tribufu.AspNetCore/Extensions/HttpContextExtensions.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: UNLICENSED
+
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Linq;
+
+namespace Tribufu.AspNetCore.Extensions
+{
+ public static class HttpContextExtensions
+ {
+ public static string GetIpAddress(this HttpContext context)
+ {
+ var headers = context.Request.Headers;
+ if (headers.TryGetValue("CF-Connecting-IP", out var cfConnectingIp))
+ {
+ return cfConnectingIp.FirstOrDefault() ?? "127.0.0.1";
+ }
+
+ if (headers.TryGetValue("X-Forwarded-For", out var xForwardedFor))
+ {
+ var forwardedIps = xForwardedFor.ToString().Split(',', StringSplitOptions.RemoveEmptyEntries);
+
+ if (forwardedIps.Length > 0)
+ {
+ return forwardedIps[0].Trim();
+ }
+ }
+
+ return context.Connection.RemoteIpAddress?.ToString() ?? "127.0.0.1";
+ }
+ }
+}
diff --git a/Source/Tribufu.AspNetCore/README.md b/Source/Tribufu.AspNetCore/README.md
new file mode 100644
index 0000000..54d4fec
--- /dev/null
+++ b/Source/Tribufu.AspNetCore/README.md
@@ -0,0 +1 @@
+# Tribufu AspNetCore
diff --git a/Source/Tribufu.AspNetCore/Tribufu.AspNetCore.csproj b/Source/Tribufu.AspNetCore/Tribufu.AspNetCore.csproj
new file mode 100644
index 0000000..a739e7f
--- /dev/null
+++ b/Source/Tribufu.AspNetCore/Tribufu.AspNetCore.csproj
@@ -0,0 +1,28 @@
+
+
+ Tribufu.AspNetCore
+ Tribufu AspNetCore
+ README.md
+
+
+ Properties
+ true
+ false
+ enable
+ Library
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/Tribufu.ComponentModel/EnumMemberConverter.cs b/Source/Tribufu.ComponentModel/EnumMemberConverter.cs
new file mode 100644
index 0000000..38d62e3
--- /dev/null
+++ b/Source/Tribufu.ComponentModel/EnumMemberConverter.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace Tribufu.ComponentModel
+{
+ public class EnumMemberConverter : EnumConverter
+ {
+ public EnumMemberConverter(Type type) : base(type) { }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ var type = typeof(T);
+
+ foreach (var field in type.GetFields())
+ {
+ if (Attribute.GetCustomAttribute(field, typeof(EnumMemberAttribute)) is EnumMemberAttribute attribute && value is string enumValue && attribute.Value == enumValue)
+ {
+ return field.GetValue(null);
+ }
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+ }
+}
diff --git a/Source/Tribufu.ComponentModel/README.md b/Source/Tribufu.ComponentModel/README.md
new file mode 100644
index 0000000..f41a146
--- /dev/null
+++ b/Source/Tribufu.ComponentModel/README.md
@@ -0,0 +1 @@
+# Tribufu ComponentModel
diff --git a/src/Tribufu/Tribufu.csproj b/Source/Tribufu.ComponentModel/Tribufu.ComponentModel.csproj
similarity index 51%
rename from src/Tribufu/Tribufu.csproj
rename to Source/Tribufu.ComponentModel/Tribufu.ComponentModel.csproj
index 39dc5af..05ddc36 100644
--- a/src/Tribufu/Tribufu.csproj
+++ b/Source/Tribufu.ComponentModel/Tribufu.ComponentModel.csproj
@@ -1,22 +1,17 @@
- Tribufu
- Tribufu .NET SDK
+ Tribufu.ComponentModel
+ Tribufu ComponentModel
README.md
Properties
- true
true
- enable
- annotations
+ false
Library
- net6.0
+ netstandard2.0;net45;net5.0
-
-
-
diff --git a/Source/Tribufu.Configuration/ConfigurationLoader.cs b/Source/Tribufu.Configuration/ConfigurationLoader.cs
new file mode 100644
index 0000000..d23c03e
--- /dev/null
+++ b/Source/Tribufu.Configuration/ConfigurationLoader.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Microsoft.Extensions.Configuration;
+using System.IO;
+using Tomlyn.Extensions.Configuration;
+using Tribufu.Logging;
+using Tribufu.Platform;
+
+namespace Tribufu.Configuration
+{
+ public static class ConfigurationLoader
+ {
+ public static IConfiguration Load(string[] fileNames)
+ {
+ var configDirectory = Paths.GetApplicationConfigDirectory();
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddEnvironmentVariables();
+
+ foreach (var fileName in fileNames)
+ {
+ var fullPath = Path.Combine(configDirectory, fileName);
+ if (!File.Exists(fullPath))
+ {
+ Logger.Debug($"Config file '{fullPath}' not found, skipping.");
+ continue;
+ }
+
+ var extension = Path.GetExtension(fullPath).ToLowerInvariant();
+ switch (extension)
+ {
+ case ".ini":
+ configurationBuilder.AddIniFile(fullPath, true, false);
+ break;
+ case ".json":
+ configurationBuilder.AddJsonFile(fullPath, true, false);
+ break;
+ case ".toml":
+ configurationBuilder.AddTomlFile(fullPath, true, false);
+ break;
+ default:
+ Logger.Warn($"Unsupported config file extension: {extension}");
+ break;
+ }
+ }
+
+ return configurationBuilder.Build();
+ }
+ }
+}
diff --git a/Source/Tribufu.Configuration/Extensions/IConfigurationExtensions.cs b/Source/Tribufu.Configuration/Extensions/IConfigurationExtensions.cs
new file mode 100644
index 0000000..6c11899
--- /dev/null
+++ b/Source/Tribufu.Configuration/Extensions/IConfigurationExtensions.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Microsoft.Extensions.Configuration;
+
+namespace Tribufu.Configuration.Extensions
+{
+ public static class IConfigurationExtensions
+ {
+ public static string? Get(this IConfiguration configuration, string prefix, string key)
+ {
+ var section = configuration.GetSection(prefix);
+ return !section.Exists() ? configuration[$"{prefix}_{key}"] : section[key];
+ }
+ }
+}
diff --git a/Source/Tribufu.Configuration/README.md b/Source/Tribufu.Configuration/README.md
new file mode 100644
index 0000000..8726fdd
--- /dev/null
+++ b/Source/Tribufu.Configuration/README.md
@@ -0,0 +1 @@
+# Tribufu Configuration
diff --git a/Source/Tribufu.Configuration/Tribufu.Configuration.csproj b/Source/Tribufu.Configuration/Tribufu.Configuration.csproj
new file mode 100644
index 0000000..3dc2aa8
--- /dev/null
+++ b/Source/Tribufu.Configuration/Tribufu.Configuration.csproj
@@ -0,0 +1,32 @@
+
+
+ Tribufu.Configuration
+ Tribufu Configuration
+ README.md
+
+
+ Properties
+ true
+ false
+ enable
+ Library
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/Tribufu.EntityFrameworkCore/IRepository.cs b/Source/Tribufu.EntityFrameworkCore/IRepository.cs
new file mode 100644
index 0000000..0e00b08
--- /dev/null
+++ b/Source/Tribufu.EntityFrameworkCore/IRepository.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Tribufu.EntityFrameworkCore
+{
+ public interface IRepository where T : class
+ {
+ void Seed();
+
+ Task SeedAsync();
+
+ IList List();
+
+ Task> ListAsync();
+
+ IList List(uint page, uint limit);
+
+ Task> ListAsync(uint page, uint limit);
+
+ T? Find(K key);
+
+ Task FindAsync(K key);
+
+ T? Create(T entity);
+
+ Task CreateAsync(T entity);
+
+ T? Update(T entity);
+
+ Task UpdateAsync(T entity);
+
+ void Delete(K key);
+
+ Task DeleteAsync(K key);
+
+ void Delete(T entity);
+
+ Task DeleteAsync(T entity);
+ }
+}
diff --git a/Source/Tribufu.EntityFrameworkCore/README.md b/Source/Tribufu.EntityFrameworkCore/README.md
new file mode 100644
index 0000000..19c48b4
--- /dev/null
+++ b/Source/Tribufu.EntityFrameworkCore/README.md
@@ -0,0 +1 @@
+# Tribufu EntityFrameworkCore
diff --git a/Source/Tribufu.EntityFrameworkCore/Repository.cs b/Source/Tribufu.EntityFrameworkCore/Repository.cs
new file mode 100644
index 0000000..6851858
--- /dev/null
+++ b/Source/Tribufu.EntityFrameworkCore/Repository.cs
@@ -0,0 +1,120 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Tribufu.EntityFrameworkCore
+{
+ public class Repository : IRepository where C : DbContext where T : class
+ {
+ protected readonly C _dbContext;
+
+ protected readonly DbSet _dbSet;
+
+ public Repository(C dbContext)
+ {
+ _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
+ _dbSet = dbContext.Set();
+ }
+
+ public virtual void Seed()
+ {
+ }
+
+ public virtual async Task SeedAsync()
+ {
+ }
+
+ public virtual IList List()
+ {
+ return [.. _dbSet];
+ }
+
+ public virtual async Task> ListAsync()
+ {
+ return await _dbSet.ToListAsync();
+ }
+
+ public virtual IList List(uint page, uint limit)
+ {
+ return _dbSet.Skip((int)((page < 1 ? 0 : page - 1) * limit)).Take((int)limit).ToList();
+ }
+
+ public virtual async Task> ListAsync(uint page, uint limit)
+ {
+ return await _dbSet.Skip((int)((page < 1 ? 0 : page - 1) * limit)).Take((int)limit).ToListAsync();
+ }
+
+ public virtual T? Find(K key)
+ {
+ return _dbSet.Find(key);
+ }
+
+ public virtual async Task FindAsync(K key)
+ {
+ return await _dbSet.FindAsync(key);
+ }
+
+ public virtual T? Create(T entity)
+ {
+ _dbSet.Add(entity);
+ var result = _dbContext.SaveChanges();
+ return result > 0 ? entity : null;
+ }
+
+ public virtual async Task CreateAsync(T entity)
+ {
+ await _dbSet.AddAsync(entity);
+ var result = await _dbContext.SaveChangesAsync();
+ return result > 0 ? entity : null;
+ }
+
+ public virtual T? Update(T entity)
+ {
+ _dbSet.Update(entity);
+ var result = _dbContext.SaveChanges();
+ return result > 0 ? entity : null;
+ }
+
+ public virtual async Task UpdateAsync(T entity)
+ {
+ _dbSet.Update(entity);
+ var result = await _dbContext.SaveChangesAsync();
+ return result > 0 ? entity : null;
+ }
+
+ public virtual void Delete(K key)
+ {
+ var entity = _dbSet.Find(key);
+ if (entity != null)
+ {
+ Delete(entity);
+ }
+ }
+
+ public virtual async Task DeleteAsync(K key)
+ {
+ var entity = await _dbSet.FindAsync(key);
+ if (entity != null)
+ {
+ await DeleteAsync(entity);
+ }
+ }
+
+ public virtual void Delete(T entity)
+ {
+ _dbSet.Remove(entity);
+ _dbContext.SaveChanges();
+ }
+
+ public virtual async Task DeleteAsync(T entity)
+ {
+ _dbSet.Remove(entity);
+ await _dbContext.SaveChangesAsync();
+ }
+ }
+}
diff --git a/Source/Tribufu.EntityFrameworkCore/Tribufu.EntityFrameworkCore.csproj b/Source/Tribufu.EntityFrameworkCore/Tribufu.EntityFrameworkCore.csproj
new file mode 100644
index 0000000..f591fd3
--- /dev/null
+++ b/Source/Tribufu.EntityFrameworkCore/Tribufu.EntityFrameworkCore.csproj
@@ -0,0 +1,26 @@
+
+
+ Tribufu.EntityFrameworkCore
+ Tribufu EntityFrameworkCore
+ README.md
+
+
+ Properties
+ true
+ false
+ enable
+ Library
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Tribufu.Example/Program.cs b/Source/Tribufu.Example/Program.cs
similarity index 66%
rename from src/Tribufu.Example/Program.cs
rename to Source/Tribufu.Example/Program.cs
index 5462790..1dce3d9 100644
--- a/src/Tribufu.Example/Program.cs
+++ b/Source/Tribufu.Example/Program.cs
@@ -1,8 +1,11 @@
// Copyright (c) Tribufu. All Rights Reserved.
-// SPDX-License-Identifier: UNLICENSED
+// SPDX-License-Identifier: MIT
using dotenv.net;
-using Tribufu.Generated.Client;
+using System;
+using System.Threading.Tasks;
+using Tribufu.Client;
+using Tribufu.Logging;
namespace Tribufu.Test
{
@@ -10,21 +13,23 @@ namespace Tribufu.Test
{
public static async Task Main(string[] args)
{
+ Logger.Initialize(LogLevel.Trace);
+
DotEnv.Load(new DotEnvOptions(ignoreExceptions: true, envFilePaths: [".env", "../../.env"]));
var apiKey = Environment.GetEnvironmentVariable("TRIBUFU_API_KEY");
var tribufu = new TribufuApi(apiKey ?? "");
- Console.WriteLine(TribufuApi.GetVersion());
+ Logger.Debug(TribufuApi.GetUserAgent());
try
{
var result = await tribufu.GetUserInfoAsync();
- Console.WriteLine(result);
+ Logger.Debug(result.ToString());
}
catch (ApiException e)
{
- Console.WriteLine(e.Message);
+ Logger.Debug(e.Message);
}
}
}
diff --git a/src/Tribufu.Example/Tribufu.Example.csproj b/Source/Tribufu.Example/Tribufu.Example.csproj
similarity index 55%
rename from src/Tribufu.Example/Tribufu.Example.csproj
rename to Source/Tribufu.Example/Tribufu.Example.csproj
index 25a4537..3bde082 100644
--- a/src/Tribufu.Example/Tribufu.Example.csproj
+++ b/Source/Tribufu.Example/Tribufu.Example.csproj
@@ -1,11 +1,16 @@
- enable
+ false
+ false
enable
Exe
net8.0
+
+
+
+
diff --git a/Source/Tribufu.Logging/LogLevel.cs b/Source/Tribufu.Logging/LogLevel.cs
new file mode 100644
index 0000000..f8cea00
--- /dev/null
+++ b/Source/Tribufu.Logging/LogLevel.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+namespace Tribufu.Logging
+{
+ public enum LogLevel : byte
+ {
+ Off = 0,
+
+ Error = 1,
+
+ Warn = 2,
+
+ Info = 3,
+
+ Debug = 4,
+
+ Trace = 5,
+ }
+}
diff --git a/Source/Tribufu.Logging/Logger.cs b/Source/Tribufu.Logging/Logger.cs
new file mode 100644
index 0000000..2da1ba9
--- /dev/null
+++ b/Source/Tribufu.Logging/Logger.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using System;
+
+namespace Tribufu.Logging
+{
+ public static class Logger
+ {
+ private static LogLevel _level = LogLevel.Off;
+
+ public static void Initialize(LogLevel level = LogLevel.Off)
+ {
+ _level = level;
+ }
+
+ public static void Info(object context, string message)
+ {
+ Info($"({context.GetType().Name}) {message}");
+ }
+
+ public static void Warn(object context, string message)
+ {
+ Warn($"({context.GetType().Name}) {message}");
+ }
+
+ public static void Error(object context, string message)
+ {
+ Error($"({context.GetType().Name}) {message}");
+ }
+
+ public static void Debug(object context, string message)
+ {
+ Debug($"({context.GetType().Name}) {message}");
+ }
+
+ public static void Trace(object context, string message)
+ {
+ Trace($"({context.GetType().Name}) {message}");
+ }
+
+ public static void Info(string message)
+ {
+ Log(LogLevel.Info, message, ConsoleColor.Green);
+ }
+
+ public static void Warn(string message)
+ {
+ Log(LogLevel.Warn, message, ConsoleColor.Yellow);
+ }
+
+ public static void Error(string message)
+ {
+ Log(LogLevel.Error, message, ConsoleColor.Red);
+ }
+
+ public static void Debug(string message)
+ {
+ Log(LogLevel.Debug, message, ConsoleColor.White);
+ }
+
+ public static void Trace(string message)
+ {
+ Log(LogLevel.Trace, message, ConsoleColor.DarkGray);
+ }
+
+ private static void Log(LogLevel level, string message, ConsoleColor color)
+ {
+ if (level <= _level)
+ {
+ var defaultColor = Console.ForegroundColor;
+ Console.ForegroundColor = color;
+ var timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
+ Console.WriteLine($"[{timestamp}] [{level.ToString().ToUpper()}]: {message}");
+ Console.ForegroundColor = defaultColor;
+ }
+ }
+ }
+}
diff --git a/Source/Tribufu.Logging/README.md b/Source/Tribufu.Logging/README.md
new file mode 100644
index 0000000..9845f6c
--- /dev/null
+++ b/Source/Tribufu.Logging/README.md
@@ -0,0 +1 @@
+# Tribufu Logging
diff --git a/Source/Tribufu.Logging/Tribufu.Logging.csproj b/Source/Tribufu.Logging/Tribufu.Logging.csproj
new file mode 100644
index 0000000..cade901
--- /dev/null
+++ b/Source/Tribufu.Logging/Tribufu.Logging.csproj
@@ -0,0 +1,19 @@
+
+
+ Tribufu.Logging
+ Tribufu Logging
+ README.md
+
+
+ Properties
+ true
+ false
+ Library
+ netstandard2.0;net45;net5.0
+
+
+
+
+
+
+
diff --git a/Source/Tribufu.Platform/FlakeGenerator.cs b/Source/Tribufu.Platform/FlakeGenerator.cs
new file mode 100644
index 0000000..ca89efd
--- /dev/null
+++ b/Source/Tribufu.Platform/FlakeGenerator.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: UNLICENSED
+
+using IdGen;
+using System;
+
+namespace Tribufu.Platform
+{
+ public static class FlakeGenerator
+ {
+ public static readonly DateTime EPOCH = new(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ private static readonly IdStructure _structure = new(41, 10, 12);
+
+ private static readonly IdGeneratorOptions _options = new(_structure, new DefaultTimeSource(EPOCH));
+
+ private static readonly IdGenerator _generator = new(0, _options);
+
+ public static ulong New()
+ {
+ //Console.WriteLine("Max. generators : {0}", _structure.MaxGenerators);
+ //Console.WriteLine("Id's/ms per generator : {0}", _structure.MaxSequenceIds);
+ //Console.WriteLine("Id's/ms total : {0}", _structure.MaxGenerators * _structure.MaxSequenceIds);
+ //Console.WriteLine("Wraparound interval : {0}", _structure.WraparoundInterval(_generator.Options.TimeSource));
+ //Console.WriteLine("Wraparound date : {0}", _structure.WraparoundDate(_generator.Options.TimeSource.Epoch, _generator.Options.TimeSource).ToString("O"));
+
+ return (ulong)_generator.CreateId();
+ }
+
+ public static ulong FromTimestamp(DateTime timestamp, ushort sequence = 0)
+ {
+ var diff = (long)(timestamp - EPOCH).TotalMilliseconds;
+ if (diff < 0 || diff > (1L << 41) - 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(timestamp), "Timestamp out of range for flake.");
+ }
+
+ ulong timestampPart = (ulong)diff << 22; // 41 bits << (10+12)
+ ulong generatorPart = 0ul << 12; // Always 0
+ ulong sequencePart = (ulong)(sequence & 0xFFF); // 12 bits
+
+ return timestampPart | generatorPart | sequencePart;
+ }
+ }
+}
diff --git a/Source/Tribufu.Platform/Paths.cs b/Source/Tribufu.Platform/Paths.cs
new file mode 100644
index 0000000..0d67e7b
--- /dev/null
+++ b/Source/Tribufu.Platform/Paths.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using Tribufu.Logging;
+
+namespace Tribufu.Platform
+{
+ ///
+ /// Provides standardized access to important directories, such as config, saved data, logs, and platform-specific binaries.
+ /// This is especially useful for abstracting file path logic across environments (development, production, etc).
+ ///
+ public static class Paths
+ {
+ ///
+ /// Gets the root base directory of the application.
+ ///
+ ///
+ /// - In development, this resolves to the root of the repository (five levels above bin/Debug or bin/Release).
+ /// - In production, it resolves to two levels above the binary location.
+ /// - It uses case-insensitive checks and runtime heuristics to improve accuracy.
+ ///
+ /// The absolute path to the base directory.
+ public static string GetApplicationDirectory()
+ {
+ try
+ {
+ string baseDirectory;
+ string defaultBaseDirectory = AppContext.BaseDirectory;
+
+ bool isDevelopment = defaultBaseDirectory.ToLowerInvariant().Contains("debug");
+
+ if (isDevelopment)
+ {
+ // Go 5 levels up to simulate project root
+ baseDirectory = Path.Combine(defaultBaseDirectory, "..", "..", "..", "..", "..");
+ }
+ else
+ {
+ baseDirectory = Path.Combine(defaultBaseDirectory, "..", "..");
+ }
+
+ return Path.GetFullPath(baseDirectory);
+ }
+ catch (Exception ex)
+ {
+ Logger.Warn($"(Paths) Failed to resolve base directory: {ex.Message}");
+ return AppContext.BaseDirectory;
+ }
+ }
+
+ ///
+ /// Gets the path to the platform-specific binary directory.
+ ///
+ ///
+ /// The absolute path to bin/<runtime-identifier> if available,
+ /// otherwise falls back to bin/dotnet.
+ ///
+ public static string GetApplicationBinDirectory()
+ {
+ var binDirectory = Path.Combine(GetApplicationDirectory(), "bin");
+
+ if (!string.IsNullOrEmpty(RuntimeInformation.RuntimeIdentifier))
+ {
+ binDirectory = Path.Combine(binDirectory, RuntimeInformation.RuntimeIdentifier);
+ }
+ else
+ {
+ binDirectory = Path.Combine(binDirectory, "dotnet");
+ }
+
+ return binDirectory;
+ }
+
+ ///
+ /// Gets the path to the configuration directory.
+ ///
+ /// The absolute path to the config directory.
+ public static string GetApplicationConfigDirectory()
+ {
+ return Path.Combine(GetApplicationDirectory(), "config");
+ }
+
+ ///
+ /// Gets the path to the assets directory.
+ ///
+ /// The absolute path to the assets directory.
+ public static string GetApplicationAssetsDirectory()
+ {
+ return Path.Combine(GetApplicationDirectory(), "assets");
+ }
+
+ ///
+ /// Gets the path to the saved data directory.
+ ///
+ /// The absolute path to the saved directory.
+ public static string GetApplicationSavedDirectory()
+ {
+ return Path.Combine(GetApplicationDirectory(), "saved");
+ }
+
+ ///
+ /// Gets the path to the cache directory inside saved.
+ ///
+ /// The absolute path to the saved/cache directory.
+ public static string GetApplicationCacheDirectory()
+ {
+ return Path.Combine(GetApplicationSavedDirectory(), "cache");
+ }
+
+ ///
+ /// Gets the path to the logs directory inside saved.
+ ///
+ /// The absolute path to the saved/logs directory.
+ public static string GetApplicationLogsDirectory()
+ {
+ return Path.Combine(GetApplicationSavedDirectory(), "logs");
+ }
+ }
+}
diff --git a/Source/Tribufu.Platform/README.md b/Source/Tribufu.Platform/README.md
new file mode 100644
index 0000000..6925ac2
--- /dev/null
+++ b/Source/Tribufu.Platform/README.md
@@ -0,0 +1 @@
+# Tribufu Runtime
diff --git a/Source/Tribufu.Platform/Tribufu.Platform.csproj b/Source/Tribufu.Platform/Tribufu.Platform.csproj
new file mode 100644
index 0000000..b1a39fa
--- /dev/null
+++ b/Source/Tribufu.Platform/Tribufu.Platform.csproj
@@ -0,0 +1,24 @@
+
+
+ Tribufu.Platform
+ Tribufu Platform
+ README.md
+
+
+ Properties
+ true
+ false
+ Library
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/Tribufu.Serialization/BaseClassFirstContractResolver.cs b/Source/Tribufu.Serialization/BaseClassFirstContractResolver.cs
new file mode 100644
index 0000000..0aa320c
--- /dev/null
+++ b/Source/Tribufu.Serialization/BaseClassFirstContractResolver.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: UNLICENSED
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Tribufu.Serialization
+{
+ public class BaseClassFirstContractResolver : DefaultContractResolver
+ {
+ protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
+ {
+ var props = base.CreateProperties(type, memberSerialization);
+ return props.OrderBy(p => { return p.DeclaringType == type ? 1 : 0; }).ToList();
+ }
+ }
+}
diff --git a/Source/Tribufu.Serialization/CustomSerializerSettings.cs b/Source/Tribufu.Serialization/CustomSerializerSettings.cs
new file mode 100644
index 0000000..b487345
--- /dev/null
+++ b/Source/Tribufu.Serialization/CustomSerializerSettings.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Serialization;
+
+namespace Tribufu.Serialization
+{
+ public static class CustomSerializerSettings
+ {
+ public static JsonSerializerSettings GetNewtonsoftJson()
+ {
+ var settings = new JsonSerializerSettings();
+ SetNewtonsoftJson(settings);
+ return settings;
+ }
+
+ public static void SetNewtonsoftJson(JsonSerializerSettings settings)
+ {
+ settings.ContractResolver = new BaseClassFirstContractResolver
+ {
+ NamingStrategy = new SnakeCaseNamingStrategy()
+ };
+
+ settings.Converters.Add(new DecimalNullableStringConverter());
+ settings.Converters.Add(new DecimalStringConverter());
+ settings.Converters.Add(new ULongNullableStringConverter());
+ settings.Converters.Add(new ULongStringConverter());
+ settings.Converters.Add(new StringEnumConverter(new SnakeCaseNamingStrategy(), false));
+
+ settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ }
+ }
+}
diff --git a/Source/Tribufu.Serialization/DecimalNullableStringConverter.cs b/Source/Tribufu.Serialization/DecimalNullableStringConverter.cs
new file mode 100644
index 0000000..fee4870
--- /dev/null
+++ b/Source/Tribufu.Serialization/DecimalNullableStringConverter.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json;
+using System;
+using System.Globalization;
+
+namespace Tribufu.Serialization
+{
+ public class DecimalNullableStringConverter : JsonConverter
+ {
+ public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.Null)
+ {
+ return null;
+ }
+
+ if (reader.TokenType == JsonToken.String || reader.TokenType == JsonToken.Integer)
+ {
+ string value = reader.Value?.ToString();
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return null;
+ }
+
+ return decimal.Parse(value);
+ }
+
+ throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing decimal?.");
+ }
+
+ public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
+ {
+ if (value.HasValue)
+ {
+ writer.WriteValue(value.Value.ToString(CultureInfo.InvariantCulture));
+ }
+ else
+ {
+ writer.WriteNull();
+ }
+ }
+ }
+}
diff --git a/Source/Tribufu.Serialization/DecimalStringConverter.cs b/Source/Tribufu.Serialization/DecimalStringConverter.cs
new file mode 100644
index 0000000..2794fa3
--- /dev/null
+++ b/Source/Tribufu.Serialization/DecimalStringConverter.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json;
+using System;
+using System.Globalization;
+
+namespace Tribufu.Serialization
+{
+ public class DecimalStringConverter : JsonConverter
+ {
+ public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.String && decimal.TryParse(reader.Value?.ToString(), out var result))
+ {
+ return result;
+ }
+
+ if (reader.TokenType == JsonToken.Integer)
+ {
+ return Convert.ToUInt64(reader.Value);
+ }
+
+ throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing decimal.");
+ }
+
+ public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
+ {
+ writer.WriteValue(value.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+}
diff --git a/Source/Tribufu.Serialization/NetworkAwareSnakeCaseNamingStrategy.cs b/Source/Tribufu.Serialization/NetworkAwareSnakeCaseNamingStrategy.cs
new file mode 100644
index 0000000..4710a14
--- /dev/null
+++ b/Source/Tribufu.Serialization/NetworkAwareSnakeCaseNamingStrategy.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json.Serialization;
+using System.Text.RegularExpressions;
+
+namespace Tribufu.Serialization
+{
+ public class NetworkAwareSnakeCaseNamingStrategy : SnakeCaseNamingStrategy
+ {
+ private static readonly string[] KnownAcronyms = new string[] { "IPv4", "IPv6" };
+
+ protected override string ResolvePropertyName(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ return name;
+ }
+
+ foreach (var acr in KnownAcronyms)
+ {
+ name = Regex.Replace(name, acr, acr.ToLower());
+ }
+
+ var snake = base.ResolvePropertyName(name);
+ foreach (var acr in KnownAcronyms)
+ {
+ var lower = acr.ToLower();
+ snake = snake.Replace(lower.Replace("_", ""), lower);
+ }
+
+ return snake;
+ }
+ }
+}
diff --git a/Source/Tribufu.Serialization/README.md b/Source/Tribufu.Serialization/README.md
new file mode 100644
index 0000000..03ac61c
--- /dev/null
+++ b/Source/Tribufu.Serialization/README.md
@@ -0,0 +1 @@
+# Tribufu Serialization
diff --git a/Source/Tribufu.Serialization/Tribufu.Serialization.csproj b/Source/Tribufu.Serialization/Tribufu.Serialization.csproj
new file mode 100644
index 0000000..6a7981a
--- /dev/null
+++ b/Source/Tribufu.Serialization/Tribufu.Serialization.csproj
@@ -0,0 +1,20 @@
+
+
+ Tribufu.Serialization
+ Tribufu Serialization
+ README.md
+
+
+ Properties
+ true
+ false
+ Library
+ netstandard2.0;net45;net5.0
+
+
+
+
+
+
+
+
diff --git a/Source/Tribufu.Serialization/ULongNullableStringConverter.cs b/Source/Tribufu.Serialization/ULongNullableStringConverter.cs
new file mode 100644
index 0000000..0beb8e4
--- /dev/null
+++ b/Source/Tribufu.Serialization/ULongNullableStringConverter.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json;
+using System;
+
+namespace Tribufu.Serialization
+{
+ public class ULongNullableStringConverter : JsonConverter
+ {
+ public override ulong? ReadJson(JsonReader reader, Type objectType, ulong? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.Null)
+ {
+ return null;
+ }
+
+ if (reader.TokenType == JsonToken.String || reader.TokenType == JsonToken.Integer)
+ {
+ string value = reader.Value?.ToString();
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return null;
+ }
+
+ return ulong.Parse(value);
+ }
+
+ throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing ulong?.");
+ }
+
+ public override void WriteJson(JsonWriter writer, ulong? value, JsonSerializer serializer)
+ {
+ if (value.HasValue)
+ {
+ writer.WriteValue(value.Value.ToString());
+ }
+ else
+ {
+ writer.WriteNull();
+ }
+ }
+ }
+}
diff --git a/Source/Tribufu.Serialization/ULongStringConverter.cs b/Source/Tribufu.Serialization/ULongStringConverter.cs
new file mode 100644
index 0000000..5cb6f33
--- /dev/null
+++ b/Source/Tribufu.Serialization/ULongStringConverter.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json;
+using System;
+
+namespace Tribufu.Serialization
+{
+ public class ULongStringConverter : JsonConverter
+ {
+ public override ulong ReadJson(JsonReader reader, Type objectType, ulong existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.String && ulong.TryParse(reader.Value?.ToString(), out var result))
+ {
+ return result;
+ }
+
+ if (reader.TokenType == JsonToken.Integer)
+ {
+ return Convert.ToUInt64(reader.Value);
+ }
+
+ throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing ulong.");
+ }
+
+ public override void WriteJson(JsonWriter writer, ulong value, JsonSerializer serializer)
+ {
+ writer.WriteValue(value.ToString());
+ }
+ }
+}
diff --git a/Source/Tribufu/Api/TribufuGeneratedApi.cs b/Source/Tribufu/Api/TribufuGeneratedApi.cs
new file mode 100644
index 0000000..096ce48
--- /dev/null
+++ b/Source/Tribufu/Api/TribufuGeneratedApi.cs
@@ -0,0 +1,20140 @@
+/*
+ * Tribufu API
+ *
+ * API to access Tribufu services.
+ *
+ * The version of the OpenAPI document: 1.4.3
+ * Contact: contact@tribufu.com
+ * Generated by: https://github.com/openapitools/openapi-generator.git
+ */
+
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Net;
+using System.Net.Mime;
+using Tribufu.Client;
+using Tribufu.Model;
+
+namespace Tribufu.Api
+{
+
+ ///
+ /// Represents a collection of functions to interact with the API endpoints
+ ///
+ public interface ITribufuGeneratedApiSync : IApiAccessor
+ {
+ #region Synchronous Operations
+ ///
+ /// Authorize the client to access the user information.
+ ///
+ ///
+ /// <b>🔒 Required permissions:</b> <code>tribufu.identity.oauth2.authorize</code>
+ ///
+ /// Thrown when fails to make API call
+ /// (optional)
+ /// Index associated with the operation.
+ /// CodeResponse
+ CodeResponse Authorize(AuthorizeRequest authorizeRequest = default(AuthorizeRequest), int operationIndex = 0);
+
+ ///
+ /// Authorize the client to access the user information.
+ ///
+ ///
+ /// <b>🔒 Required permissions:</b> <code>tribufu.identity.oauth2.authorize</code>
+ ///
+ /// Thrown when fails to make API call
+ /// (optional)
+ /// Index associated with the operation.
+ /// ApiResponse of CodeResponse
+ ApiResponse AuthorizeWithHttpInfo(AuthorizeRequest authorizeRequest = default(AuthorizeRequest), int operationIndex = 0);
+ ///
+ /// Change the email of a user.
+ ///
+ ///
+ /// This endpoint is not available with an api key, only with a bearer token.<br/><br/><b>🔒 Required permissions:</b> <code>tribufu.identity.user.email.update</code>
+ ///
+ /// Thrown when fails to make API call
+ ///
+ /// (optional)
+ /// Index associated with the operation.
+ ///
+ void ChangeEmail(string id, Object body = default(Object), int operationIndex = 0);
+
+ ///
+ /// Change the email of a user.
+ ///
+ ///
+ /// This endpoint is not available with an api key, only with a bearer token.<br/><br/><b>🔒 Required permissions:</b> <code>tribufu.identity.user.email.update</code>
+ ///
+ /// Thrown when fails to make API call
+ ///
+ /// (optional)
+ /// Index associated with the operation.
+ /// ApiResponse of Object(void)
+ ApiResponse