Browse Source

My own version of the trackmania.io module

new_auth
Quildra 2 years ago
parent
commit
844f27af59
  1. 48
      .gitignore
  2. 2487
      package-lock.json
  3. 42
      packages/bridge-shared/.gitignore
  4. 21
      packages/bridge-shared/trackmania.io-f/.editorconfig
  5. 3
      packages/bridge-shared/trackmania.io-f/.eslintignore
  6. 21
      packages/bridge-shared/trackmania.io-f/.eslintrc.json
  7. 76
      packages/bridge-shared/trackmania.io-f/.github/CODE_OF_CONDUCT.md
  8. 1
      packages/bridge-shared/trackmania.io-f/.github/FUNDING.yml
  9. 35
      packages/bridge-shared/trackmania.io-f/.github/workflows/build.yml
  10. 46
      packages/bridge-shared/trackmania.io-f/.github/workflows/publish-dev.yml
  11. 51
      packages/bridge-shared/trackmania.io-f/.github/workflows/publish.yml
  12. 35
      packages/bridge-shared/trackmania.io-f/.github/workflows/release-at-merge-main.yml
  13. 34
      packages/bridge-shared/trackmania.io-f/.github/workflows/unit-tests.yml
  14. 30
      packages/bridge-shared/trackmania.io-f/.vscode/launch.json
  15. 27
      packages/bridge-shared/trackmania.io-f/.vscode/tasks.json
  16. 37
      packages/bridge-shared/trackmania.io-f/CONTRIBUTING.md
  17. 674
      packages/bridge-shared/trackmania.io-f/LICENSE
  18. 92
      packages/bridge-shared/trackmania.io-f/README.md
  19. 7
      packages/bridge-shared/trackmania.io-f/docs/README.md
  20. BIN
      packages/bridge-shared/trackmania.io-f/docs/graphic/Lobster-Regular.ttf
  21. 1
      packages/bridge-shared/trackmania.io-f/docs/graphic/fontsvg.svg
  22. 1
      packages/bridge-shared/trackmania.io-f/docs/graphic/heartbeat-solid.svg
  23. 13
      packages/bridge-shared/trackmania.io-f/docs/graphic/image.svg
  24. 26
      packages/bridge-shared/trackmania.io-f/docs/index.yml
  25. 70
      packages/bridge-shared/trackmania.io-f/examples/errorCatches.md
  26. 18
      packages/bridge-shared/trackmania.io-f/examples/officialCampaign.md
  27. 19
      packages/bridge-shared/trackmania.io-f/examples/playerCOTD.md
  28. 16
      packages/bridge-shared/trackmania.io-f/examples/totd.md
  29. 15
      packages/bridge-shared/trackmania.io-f/examples/totdEvent.md
  30. 39
      packages/bridge-shared/trackmania.io-f/package.json
  31. 100
      packages/bridge-shared/trackmania.io-f/src/client/BaseClient.js
  32. 169
      packages/bridge-shared/trackmania.io-f/src/client/Client.js
  33. 125
      packages/bridge-shared/trackmania.io-f/src/data/MatchmakingDivisions.json
  34. 42
      packages/bridge-shared/trackmania.io-f/src/data/PlayerEchelons.json
  35. 82
      packages/bridge-shared/trackmania.io-f/src/data/RoyalTeams.json
  36. 5
      packages/bridge-shared/trackmania.io-f/src/index.js
  37. 101
      packages/bridge-shared/trackmania.io-f/src/managers/AdsManager.js
  38. 200
      packages/bridge-shared/trackmania.io-f/src/managers/COTDManager.js
  39. 62
      packages/bridge-shared/trackmania.io-f/src/managers/CacheManager.js
  40. 204
      packages/bridge-shared/trackmania.io-f/src/managers/CampaignManager.js
  41. 101
      packages/bridge-shared/trackmania.io-f/src/managers/ClubManager.js
  42. 142
      packages/bridge-shared/trackmania.io-f/src/managers/EventManager.js
  43. 65
      packages/bridge-shared/trackmania.io-f/src/managers/MapManager.js
  44. 132
      packages/bridge-shared/trackmania.io-f/src/managers/MatchesManager.js
  45. 131
      packages/bridge-shared/trackmania.io-f/src/managers/NewsManager.js
  46. 304
      packages/bridge-shared/trackmania.io-f/src/managers/PlayerManager.js
  47. 149
      packages/bridge-shared/trackmania.io-f/src/managers/RoomManager.js
  48. 99
      packages/bridge-shared/trackmania.io-f/src/managers/TOTDManager.js
  49. 124
      packages/bridge-shared/trackmania.io-f/src/rest/APIRequest.js
  50. 96
      packages/bridge-shared/trackmania.io-f/src/structures/Ad.js
  51. 71
      packages/bridge-shared/trackmania.io-f/src/structures/COTD.js
  52. 455
      packages/bridge-shared/trackmania.io-f/src/structures/Campaign.js
  53. 385
      packages/bridge-shared/trackmania.io-f/src/structures/Club.js
  54. 353
      packages/bridge-shared/trackmania.io-f/src/structures/Match.js
  55. 104
      packages/bridge-shared/trackmania.io-f/src/structures/MatchmakingDivision.js
  56. 1303
      packages/bridge-shared/trackmania.io-f/src/structures/Player.js
  57. 163
      packages/bridge-shared/trackmania.io-f/src/structures/Room.js
  58. 71
      packages/bridge-shared/trackmania.io-f/src/structures/Splashscreen.js
  59. 636
      packages/bridge-shared/trackmania.io-f/src/structures/TMEvent.js
  60. 480
      packages/bridge-shared/trackmania.io-f/src/structures/TMMap.js
  61. 85
      packages/bridge-shared/trackmania.io-f/src/structures/TOTD.js
  62. 116
      packages/bridge-shared/trackmania.io-f/src/util/Constants.js
  63. 31
      packages/bridge-shared/trackmania.io-f/src/util/ReqUtil.js
  64. 34
      packages/bridge-shared/trackmania.io-f/src/util/Util.js
  65. 421
      packages/bridge-shared/trackmania.io-f/src/util/defaultOptions.js
  66. 31
      packages/bridge-shared/trackmania.io-f/test/campaigns.test.js
  67. 30
      packages/bridge-shared/trackmania.io-f/test/clubs.test.js
  68. 15
      packages/bridge-shared/trackmania.io-f/test/events.test.js
  69. 22
      packages/bridge-shared/trackmania.io-f/test/map.test.js
  70. 65
      packages/bridge-shared/trackmania.io-f/test/players.test.js
  71. 24
      packages/bridge-shared/trackmania.io-f/test/rooms.test.js
  72. 30
      packages/bridge-shared/trackmania.io-f/test/totd.test.js
  73. 9
      packages/bridge-shared/trackmania.io-f/tsconfig.json
  74. 74
      packages/bridge-shared/trackmania.io-f/typings/client/BaseClient.d.ts
  75. 92
      packages/bridge-shared/trackmania.io-f/typings/client/Client.d.ts
  76. 1
      packages/bridge-shared/trackmania.io-f/typings/index.d.ts
  77. 46
      packages/bridge-shared/trackmania.io-f/typings/managers/AdsManager.d.ts
  78. 122
      packages/bridge-shared/trackmania.io-f/typings/managers/COTDManager.d.ts
  79. 39
      packages/bridge-shared/trackmania.io-f/typings/managers/CacheManager.d.ts
  80. 114
      packages/bridge-shared/trackmania.io-f/typings/managers/CampaignManager.d.ts
  81. 54
      packages/bridge-shared/trackmania.io-f/typings/managers/ClubManager.d.ts
  82. 94
      packages/bridge-shared/trackmania.io-f/typings/managers/EventManager.d.ts
  83. 40
      packages/bridge-shared/trackmania.io-f/typings/managers/MapManager.d.ts
  84. 82
      packages/bridge-shared/trackmania.io-f/typings/managers/MatchesManager.d.ts
  85. 47
      packages/bridge-shared/trackmania.io-f/typings/managers/NewsManager.d.ts
  86. 180
      packages/bridge-shared/trackmania.io-f/typings/managers/PlayerManager.d.ts
  87. 101
      packages/bridge-shared/trackmania.io-f/typings/managers/RoomManager.d.ts
  88. 47
      packages/bridge-shared/trackmania.io-f/typings/managers/TOTDManager.d.ts
  89. 42
      packages/bridge-shared/trackmania.io-f/typings/rest/APIRequest.d.ts
  90. 64
      packages/bridge-shared/trackmania.io-f/typings/structures/Ad.d.ts
  91. 50
      packages/bridge-shared/trackmania.io-f/typings/structures/COTD.d.ts
  92. 289
      packages/bridge-shared/trackmania.io-f/typings/structures/Campaign.d.ts
  93. 239
      packages/bridge-shared/trackmania.io-f/typings/structures/Club.d.ts
  94. 225
      packages/bridge-shared/trackmania.io-f/typings/structures/Match.d.ts
  95. 64
      packages/bridge-shared/trackmania.io-f/typings/structures/MatchmakingDivision.d.ts
  96. 845
      packages/bridge-shared/trackmania.io-f/typings/structures/Player.d.ts
  97. 99
      packages/bridge-shared/trackmania.io-f/typings/structures/Room.d.ts
  98. 49
      packages/bridge-shared/trackmania.io-f/typings/structures/Splashscreen.d.ts
  99. 397
      packages/bridge-shared/trackmania.io-f/typings/structures/TMEvent.d.ts
  100. 300
      packages/bridge-shared/trackmania.io-f/typings/structures/TMMap.d.ts

48
.gitignore

@ -1,3 +1,47 @@
node_modules/
packages/bridge-server/data/data.db
packages/bridge-server/upload
packages/bridge-server/upload
/.angular
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

2487
package-lock.json

File diff suppressed because it is too large

42
packages/bridge-shared/.gitignore

@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

21
packages/bridge-shared/trackmania.io-f/.editorconfig

@ -0,0 +1,21 @@
root = true
[*]
indent_style = space
end_of_line = lf
insert_final_newline = false
[*.md]
indent_size = 2
[*.js]
indent_size = 4
[*.yml]
indent_size = 2
[*.yaml]
indent_size = 2
[*.json]
indent_size = 4

3
packages/bridge-shared/trackmania.io-f/.eslintignore

@ -0,0 +1,3 @@
test/
node_modules/
.github/

21
packages/bridge-shared/trackmania.io-f/.eslintrc.json

@ -0,0 +1,21 @@
{
"env": {
"commonjs": true,
"es2021": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 12
},
"rules": {
"indent": [
"error",
4
],
"semi": [
"error",
"always"
]
}
}

76
packages/bridge-shared/trackmania.io-f/.github/CODE_OF_CONDUCT.md

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at https://greep.fr/discord. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

1
packages/bridge-shared/trackmania.io-f/.github/FUNDING.yml

@ -0,0 +1 @@
github: GreepTheSheep

35
packages/bridge-shared/trackmania.io-f/.github/workflows/build.yml

@ -0,0 +1,35 @@
name: Builds
on:
push:
branches-ignore:
- 'docs'
tags:
- '*'
paths-ignore:
- 'typings/**'
workflow_dispatch:
permissions: write-all
jobs:
docs:
name: Build Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
cache: npm
- name: Install dependencies
run: npm ci
- name: Build and deploy documentation
uses: discordjs/action-docs@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

46
packages/bridge-shared/trackmania.io-f/.github/workflows/publish-dev.yml

@ -0,0 +1,46 @@
name: Publish dev
on:
workflow_dispatch:
schedule:
- cron: '0 0 */4 * *'
jobs:
npm:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
ref: 'develop'
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org/
cache: npm
- name: Check previous released version
id: pre-release
run: |
if [[ $(npm view trackmania.io@dev version | grep -e "$(jq --raw-output '.version' package.json)-dev.*.$(git rev-parse --short HEAD | cut -b1-3)") ]]; \
then echo '::set-output name=release::false'; \
else echo '::set-output name=release::true'; fi
- name: Install dependencies
if: steps.pre-release.outputs.release == 'true'
run: npm ci --ignore-scripts
- name: Deprecate old dev version
if: steps.pre-release.outputs.release == 'true'
run: "npm deprecate trackmania.io@$(npm view trackmania.io@dev version) 'no longer supported' || true"
env:
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
- name: Publish
if: steps.pre-release.outputs.release == 'true'
run: |
npm version --git-tag-version=false $(jq --raw-output '.version' package.json)-dev.$(date +%d%m%y).$(git rev-parse --short HEAD)
npm publish --tag dev || true
env:
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}

51
packages/bridge-shared/trackmania.io-f/.github/workflows/publish.yml

@ -0,0 +1,51 @@
name: Publish on Release created
on:
release:
types: [created]
jobs:
publish-npm:
name: Publish on NPM
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js 16
uses: actions/setup-node@v1
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- name: Install dependencies
run: npm ci
- name: Publish on NPM
run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
publish-gpr:
name: Publish on Github Packages
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js 16
uses: actions/setup-node@v1
with:
node-version: 16
registry-url: https://npm.pkg.github.com/
scope: '@greepthesheep'
- name: Install dependencies
run: npm ci
- name: Publish on Github Packages
run: npm publish --registry=https://npm.pkg.github.com/@greepthesheep
env:
NODE_AUTH_TOKEN: ${{secrets.gh_token}}

35
packages/bridge-shared/trackmania.io-f/.github/workflows/release-at-merge-main.yml

@ -0,0 +1,35 @@
name: Release after PR to main
on:
pull_request:
branches:
- main
types: [closed]
jobs:
release:
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.merged }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Get Version from package.json
id: version
uses: notiz-dev/github-action-json-property@release
with:
path: 'package.json'
prop_path: 'version'
- name: Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{secrets.gh_token}}
with:
tag_name: v${{steps.version.outputs.prop}}
release_name: Release ${{steps.version.outputs.prop}}
body: ${{ github.event.pull_request.body }}
draft: false
prerelease: false

34
packages/bridge-shared/trackmania.io-f/.github/workflows/unit-tests.yml

@ -0,0 +1,34 @@
name: Unit Tests
on:
pull_request:
branches:
- main
- develop
paths:
- 'src/**.js'
- 'test/**.test.js'
workflow_dispatch:
jobs:
test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v16
uses: actions/setup-node@v2
with:
node-version: 16
cache: npm
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
env:
TMIO_API: ${{ secrets.TMIO_API }}

30
packages/bridge-shared/trackmania.io-f/.vscode/launch.json

@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Program",
"program": "${file}",
"cwd": "${workspaceFolder}",
"request": "launch",
"skipFiles": [
"<node_internals>/**"
],
"type": "pwa-node"
},
{
"type": "node",
"request": "launch",
"name": "ESLint + Mocha",
"runtimeExecutable": "npm",
"runtimeArgs": [
"test"
],
"skipFiles": [
"<node_internals>/**"
]
}
]
}

27
packages/bridge-shared/trackmania.io-f/.vscode/tasks.json

@ -0,0 +1,27 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "dts",
"problemMatcher": [],
"label": "npm: dts",
"detail": "tsc",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "test",
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": [],
"label": "npm: test",
"detail": "eslint . && mocha"
}
]
}

37
packages/bridge-shared/trackmania.io-f/CONTRIBUTING.md

@ -0,0 +1,37 @@
# Contributing (v3)
Thanks for being part of the code!
## Directory layout
```css
+---.github
¦ +---workflows /* CI & automations for PR and releases */
+---.vscode /* VSCode Debug and tasks file */
+---node_modules /* Node.js modules, for npm i */
+---src
¦ +---client
¦ +---data /* The data file that can be used for your views, containing useful strings */
¦ +---managers
¦ +---rest /* HTTP Requests */
¦ +---structures
¦ +---util /* Client options and private constructor strings */
+---typings /* TypeScript auto-generated typings for VSCode and other IDE using TS */
+---test /* Mocha tests (coming later in v3) */
```
## Pull Requests
Before opening a PR, run `npm test` to do a eslint scan then run mocha tests
Before commiting, **be sure that no files from the `typings/` directory is commited!** GitHub Actions will build it automatically after pushing
When opening a PR, please open it to the 'develop' branch. Thanks!
## Issues
All issues are appreciated, it can be suggestions, bug fixes.
## Discussion
If you want to talk about this project or just for saying hi, Join the [Greep's Discord Server](https://greep.gq/discord) in the #node-tmio channel or the [Openplanet Discord Server](https://openplanet.nl/link/discord), in the #trackmania-io channel

674
packages/bridge-shared/trackmania.io-f/LICENSE

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

92
packages/bridge-shared/trackmania.io-f/README.md

@ -0,0 +1,92 @@
<div align="center">
[![Logo](https://raw.githubusercontent.com/GreepTheSheep/node-trackmania.io/main/docs/graphic/image.svg)](https://tmio.greep.fr/#)
[![NPM](https://nodei.co/npm/trackmania.io.png?downloads=true&stars=true)](https://npmjs.org/trackmania.io)
[![npm](https://img.shields.io/npm/v/trackmania.io?logo=npm)](https://npmjs.com/trackmania.io)
[![GitHub release](https://img.shields.io/github/v/release/GreepTheSheep/node-trackmania.io?logo=github)](https://github.com/GreepTheSheep/node-trackmania.io/releases/latest)
[![Trackmania.io API Status](https://img.shields.io/website?down_message=Offline&label=Trackmania.io%20API&up_message=Online&url=https%3A%2F%2Ftrackmania.io)](https://trackmania.io)
[![Discord](https://img.shields.io/discord/570024448371982373?label=Discord&logo=discord)](https://greep.fr/discord)
[![npm](https://img.shields.io/npm/dw/trackmania.io?logo=npm)](https://npmjs.com/trackmania.io)
[![NPM](https://img.shields.io/npm/l/trackmania.io)](LICENSE)
[![GitHub issues](https://img.shields.io/github/issues/GreepTheSheep/node-trackmania.io?logo=github)](https://github.com/GreepTheSheep/node-trackmania.io/issues)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/GreepTheSheep/node-trackmania.io?logo=github)](https://github.com/GreepTheSheep/node-trackmania.io/pulls)
[![GitHub Repo stars](https://img.shields.io/github/stars/GreepTheSheep/node-trackmania.io?logo=github&style=flat-square)](https://github.com/GreepTheSheep/node-trackmania.io/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/GreepTheSheep/node-trackmania.io?style=flat-square)](https://github.com/GreepTheSheep/node-trackmania.io/network/members)
[![Builds](https://github.com/GreepTheSheep/node-trackmania.io/actions/workflows/build.yml/badge.svg)](https://github.com/GreepTheSheep/node-trackmania.io/actions/workflows/build.yml)
</div>
## Disclaimer - [Can I use the Trackmania.io API for my own project?](https://openplanet.nl/tmio/api)
[Please read this topic](https://openplanet.nl/tmio/api)
*The User Agent Header will be automatically set to your project directory name, with your current file name included, you can also set your User Agent with the method `Client.setUserAgent("My Project Name");`*
API Keys can be set with the method `Client.setAPIKey("yourname:yourkey");`
If you're still unsure about your use of the API, feel free to DM Miss on the [Openplanet Discord](https://openplanet.nl/link/discord) with any further questions.
---
## API Keys
API keys for trackmania.io are not mandatory, but can be used to increase the rate limit. These are not given out frequently, and as such are issued only on request. For more information, contact Miss on the [Openplanet Discord](https://openplanet.nl/link/discord).
---
## Documentation
Documentation and examples are available on [this website](https://tmio.greep.fr/#/docs)
---
## Contributing
Please see the [CONTRIBUTING.md file](CONTRIBUTING.md)
---
## Discussion
If you want to talk about this project or just for saying hi, Join the [Project's Discord Server](https://greep.fr/discord), in the #node-tmio channel
---
## Projects that use this Trackmania.io library
- [discord-trackmania.io](https://github.com/GreepTheSheep/discord-trackmania.io) by [@GreepTheSheep](https://github.com/GreepTheSheep)
- [state-trackmania-bot](https://github.com/allanjacob/state-trackmania-bot) by [@allanjacob](https://github.com/allanjacob)
Add yours here by opening a PR!
---
## Other Trackmania.io Implementations
There are a few other implementations of the Trackmania.io API, these are:
- Python: [py-tmio](https://github.com/NottCurious/py-tmio) by [@NottCurious](https://github.com/NottCurious)
- C# (.NET): [TrackmaniaIo.ApiClient](https://github.com/snixtho/TrackmaniaIo.ApiClient) by [@snixtho](https://github.com/snixtho)
Add yours here by opening a PR!
---
## Thanks
Thanks to [@codecat](https://github.com/codecat) for creating trackmania.io and for helping me with their API *(and updating it everytime)*.
Thanks to Nadeo (Trackmania's studio) for giving an API to trackmania.io.
Thanks to [@dassschaf](https://github.com/dassschaf) and [@stefan-baumann](https://github.com/stefan-baumann) for chat formatting remover code in regex *(it saves my life)*
Thanks to [@Flirno](https://github.com/Flirno) for player COTD results API
Thanks to [@jonese1234](https://github.com/jonese1234) for the dataset to get the players searching
---
<div align="center">
![Alt](https://repobeats.axiom.co/api/embed/6d1745e4fe894ffc1e84f68bc7eb0731588eeda4.svg "Repobeats analytics image")
</div>

7
packages/bridge-shared/trackmania.io-f/docs/README.md

@ -0,0 +1,7 @@
### This folder contains the configuration file for building the JSDoc, which is used to create the documentation for the website.
## **[View the website](https://tmio.greep.fr/)**
[View the source code of the website](https://github.com/GreepTheSheep/node-trackmania.io-website/)
[View the JSDoc Output](https://github.com/GreepTheSheep/node-trackmania.io/tree/docs)

BIN
packages/bridge-shared/trackmania.io-f/docs/graphic/Lobster-Regular.ttf

Binary file not shown.

1
packages/bridge-shared/trackmania.io-f/docs/graphic/fontsvg.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 31 KiB

1
packages/bridge-shared/trackmania.io-f/docs/graphic/heartbeat-solid.svg

@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="heartbeat" class="svg-inline--fa fa-heartbeat fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M320.2 243.8l-49.7 99.4c-6 12.1-23.4 11.7-28.9-.6l-56.9-126.3-30 71.7H60.6l182.5 186.5c7.1 7.3 18.6 7.3 25.7 0L451.4 288H342.3l-22.1-44.2zM473.7 73.9l-2.4-2.5c-51.5-52.6-135.8-52.6-187.4 0L256 100l-27.9-28.5c-51.5-52.7-135.9-52.7-187.4 0l-2.4 2.4C-10.4 123.7-12.5 203 31 256h102.4l35.9-86.2c5.4-12.9 23.6-13.2 29.4-.4l58.2 129.3 49-97.9c5.9-11.8 22.7-11.8 28.6 0l27.6 55.2H481c43.5-53 41.4-132.3-7.3-182.1z"></path></svg>

After

Width:  |  Height:  |  Size: 644 B

13
packages/bridge-shared/trackmania.io-f/docs/graphic/image.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

26
packages/bridge-shared/trackmania.io-f/docs/index.yml

@ -0,0 +1,26 @@
- name: General
files:
- name: Welcome
id: welcome
path: ../../README.md
- name: Contributing
id: contributing
path: ../../CONTRIBUTING.md
- name: Examples
files:
- name: Today's TOTD
id: totd
path: ../../examples/totd.md
- name: TOTD alert
id: totdEvent
path: ../../examples/totdEvent.md
- name: Player's COTD stats
id: playerCOTD
path: ../../examples/playerCOTD.md
- name: Current official campaign
id: currentCampaign
path: ../../examples/officialCampaign.md
- name: Gracefully catch errors
id: errorCatches
path: ../../examples/errorCatches.md

70
packages/bridge-shared/trackmania.io-f/examples/errorCatches.md

@ -0,0 +1,70 @@
## Gracefully catches errors
Every error is sent trough a `throw`, meaning that errors is handeled with a `.catch()` block or in a try/catch way.
Most errors can come from an API block.
Every requests is managed with a Promise, so you need to handle that with async/await or with a `.then()` block
### Example with main request
```js
const TMIO = require('trackmania.io'),
client = new TMIO.Client();
client.campaigns.currentSeason().then(async campaign=>{
console.log('The actual official campaign is', campaign.name);
console.log('The image URL of this campaign is:', campaign.image);
try {
// Here, we try to execute another request, but in a async way
// so we need to use try/catch
console.log('Top 10 of this campaign:');
const leaderboard = await campaign.leaderboard();
leaderboard.forEach(top=>{
console.log(top.position, top.playerName, "with", top.points, "points");
});
} catch (err) {
// Here, an script or API error is throwed
console.error(err);
// Mostly, if it's an API block, you'll have this message:
// "You are blocked from the API. Please get in touch with @Miss#8888 on Discord: https://openplanet.nl/link/discord - or DM me on Twitter: https://twitter.com/codecatt -- For more information on third-party API usage, see the following page: https://openplanet.nl/tmio/api"
}
}).catch(err=>{
// An error, sadge
// Meanwhile, this is the same as a try/catch
console.error(err);
})
```
### Example with side requests
Side requests is requests made to another domain than trackmania.io (trackmania.exchange for example), this does not throws a error but emit an "error" Event.
Error events is not a big deal than main errors
To catch them, you just need to add a `client.on("error", error=>...)` block
```js
const TMIO = require('trackmania.io'),
client = new TMIO.Client();
client.maps.get("89OtLgP9IRQzmC9n_h0SrIr8l_4").then(map=>{
console.log(map.name);
}).catch(err=>{
// Here, we got a tm.io error, because it's the main thing after all
console.error(err);
});
client.on("error", error=>{
// But here, errors can come from another services for the map info
// Trackmania Exchange for example
console.error(error);
});
```

18
packages/bridge-shared/trackmania.io-f/examples/officialCampaign.md

@ -0,0 +1,18 @@
## Get the actual official Campaign
```js
const TMIO = require('trackmania.io'),
client = new TMIO.Client();
client.campaigns.currentSeason().then(async campaign=>{
console.log('The actual official campaign is', campaign.name);
console.log('The image URL of this campaign is:', campaign.image);
console.log('Top 10 of this campaign:');
const leaderboard = await campaign.leaderboard();
leaderboard.forEach(top=>{
console.log(top.position, top.playerName, "with", top.points, "points");
});
});
```

19
packages/bridge-shared/trackmania.io-f/examples/playerCOTD.md

@ -0,0 +1,19 @@
## Get Player's COTD stats
```js
const TMIO = require('trackmania.io'),
client = new TMIO.Client();
client.players.get("26d9a7de-4067-4926-9d93-2fe62cd869fc").then(async player=>{
const cotd = await player.cotd();
console.log(`
${player.name}'s COTD stats:
${cotd.count} Cup Of The Day played,
${cotd.stats.totalDivWins} wins in any division,
${(cotd.stats.averageDivRank * 100).toFixed(2)}% average rank,
The best division in overall was Div ${cotd.stats.bestOverall.division},
The best division in primary COTD was Div ${cotd.stats.bestPrimary.division},
`);
});
```

16
packages/bridge-shared/trackmania.io-f/examples/totd.md

@ -0,0 +1,16 @@
## Get Today's Track Of The Day
```js
const TMIO = require('trackmania.io'),
client = new TMIO.Client();
client.totd.get(new Date()).then(async totd=>{
const map = await totd.map(),
author = await map.author();
// Map names aren't formatted by default (color codes for example), so we need to format them
const mapName = client.stripFormat(map.name);
console.log("Today's TOTD is called", mapName, "and it was created by", author.name);
});
```

15
packages/bridge-shared/trackmania.io-f/examples/totdEvent.md

@ -0,0 +1,15 @@
## Gets the TOTD when it cames out every day
```js
const TMIO = require('trackmania.io'),
client = new TMIO.Client();
client.on('totd', async totd=>{
const map = await totd.map();
// Map names aren't formatted by default (color codes for example), so we need to format them
const mapName = client.stripFormat(map.name);
console.log('New Track Of The Day:', mapName);
});
```

39
packages/bridge-shared/trackmania.io-f/package.json

@ -0,0 +1,39 @@
{
"name": "trackmania.io",
"version": "3.2.2",
"description": "Node.js inplementation of Trackmania Live services (trackmania.io)",
"main": "src/index.js",
"types": "typings/index.d.ts",
"scripts": {
"test": "eslint . && mocha --exit",
"prepack": "tsc",
"dts": "tsc",
"docs": "docgen --source src --custom docs/index.yml --output docs/docs.json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/GreepTheSheep/node-trackmania.io.git"
},
"keywords": [
"trackmania",
"api",
"services"
],
"author": "Greep",
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/GreepTheSheep/node-trackmania.io/issues"
},
"homepage": "https://tmio.greep.gq/",
"dependencies": {
"luxon": "^3.3.0",
"node-fetch": "2.6.7"
},
"devDependencies": {
"@discordjs/docgen": "0.11.1",
"dotenv": "16.1.4",
"eslint": "7.31.0",
"mocha": "10.2.0",
"typescript": "5.1.3"
}
}

100
packages/bridge-shared/trackmania.io-f/src/client/BaseClient.js

@ -0,0 +1,100 @@
const EventEmitter = require('events');
const APIRequest = require('../rest/APIRequest');
const Util = require('../util/Util');
const defaultOptions = require('../util/defaultOptions');
/**
* The Base Client
* @extends {EventEmitter}
*/
class BaseClient extends EventEmitter {
constructor(options = {}) {
super();
/**
* The options of the client.
* @type {defaultOptions}
* @readonly
*/
this.options = Util.mergeDefault(new defaultOptions(this), options);
/**
* Get the ratelimits details on trackmania.io.
* @type {ClientRatelimit}
*/
this.ratelimit = new ClientRatelimit(this);
}
/**
* Do an API request
* @param {string} url The URL to request
* @param {string} [method="GET"] The HTTP method to use
* @param {?Object} [body=null] The data to send
* @returns {Promise<Object>} A promise that resolves to the API response
* @private
*/
async _apiReq(url, method = 'GET', body = null){
const request = new APIRequest(this);
return await request.do(url, method, body);
}
/**
* Sets a User Agent for your project.
* Required if you run a important project.
* @param {string} [useragent=this.options.api.useragent] The User Agent to set to.
* @returns {void}
*/
setUserAgent(useragent = this.options.api.useragent){
this.options.api.useragent = useragent;
}
/**
* Sets an API Key.
* Required if you run a important project. It will triple the rate limit.
*
* <warn>This should be kept private at all times.</warn>
* @param {string} [key=this.options.api.key] The full API key. It must contains "yourname:theactualsecretkey"
* @returns {void}
*/
setAPIKey(key = this.options.api.key){
this.options.api.key = key;
}
}
/**
* The ratelimit details of the client.
*/
class ClientRatelimit {
constructor(baseClient){
/**
* The base client.
* @type {BaseClient}
*/
this.baseClient = baseClient;
/**
* The total number of requests you can make on trackmania.io API.
* If null, it means you haven't actually done a request
* @type {?number}
*/
this.ratelimit = null;
/**
* The number of requests you can make before the ratelimit resets.
* If null, it means you haven't actually done a request
* @type {?number}
*/
this.remaining = null;
/**
* The date when the ratelimit resets.
* If null, it means you haven't actually done a request
* @type {?Date}
*/
this.reset = null;
}
}
module.exports = BaseClient;

169
packages/bridge-shared/trackmania.io-f/src/client/Client.js

@ -0,0 +1,169 @@
const {DateTime} = require('luxon');
const BaseClient = require('./BaseClient');
const { deprecate } = require('util');
const defaultOptions = require('../util/defaultOptions'); // eslint-disable-line no-unused-vars
const TOTD = require('../structures/TOTD'); // eslint-disable-line no-unused-vars
// Managers
const PlayerManager = require('../managers/PlayerManager');
const MapManager = require('../managers/MapManager');
const TOTDManager = require('../managers/TOTDManager');
const COTDManager = require('../managers/COTDManager');
const ClubManager = require('../managers/ClubManager');
const CampaignManager = require('../managers/CampaignManager');
const RoomManager = require('../managers/RoomManager');
const EventManager = require('../managers/EventManager');
const NewsManager = require('../managers/NewsManager');
const AdsManager = require('../managers/AdsManager');
const MatchesManager = require('../managers/MatchesManager');
/**
* Instantiates a new client. This is the entry point.
* @extends {BaseClient}
*/
class Client extends BaseClient {
/**
* @param {defaultOptions} [options={}] The options to use
*/
constructor(options){
super(options);
/**
* The player manager
* @type {PlayerManager}
*/
this.players = new PlayerManager(this);
/**
* The map manager
* @type {MapManager}
*/
this.maps = new MapManager(this);
/**
* The TOTD manager
* @type {TOTDManager}
*/
this.totd = new TOTDManager(this);
/**
* The COTD manager
* @type {COTDManager}
*/
this.cotd = new COTDManager(this);
/**
* The club manager
* @type {ClubManager}
*/
this.clubs = new ClubManager(this);
/**
* The campaign manager
* @type {CampaignManager}
*/
this.campaigns = new CampaignManager(this);
/**
* The room manager
* @type {RoomManager}
*/
this.rooms = new RoomManager(this);
/**
* The TM events manager
* @type {EventManager}
*/
this.events = new EventManager(this);
/**
* The matches manager
* @type {MatchesManager}
*/
this.matches = new MatchesManager(this);
/**
* The news manager
* @type {NewsManager}
*/
this.news = new NewsManager(this);
/**
* The Maniapub manager
* @type {AdsManager}
*/
this.ads = new AdsManager(this);
// Will initialize the TOTD event, witch calls an event for a new TOTD every day at 19h Europe/Paris timezone
let newTotdChecked = false;
setInterval(async ()=>{
const date = DateTime.local().setZone("Europe/Paris");
if (date.hour === 19 && !newTotdChecked){
let totd = await this.totd.get(date.toJSDate());
if (totd.monthDay === date.day && totd.month === date.month && totd.year === date.year) {
/**
* Emitted when a new Track Of The Day is out on Trackmania.io.
* @event Client#totd
* @param {TOTD} totd The Track of The Day
*/
this.emit('totd', totd);
// this prevent emitting this event a second time in the same day
newTotdChecked = true;
}
}
if (date.hour !== 19) newTotdChecked = false;
}, 30000);
}
/**
* Format the string and remove the TM style code on it.
* @param {string} str string to format
* @returns {string}
* @deprecated use {@link Client#stripFormat} instead
*/
formatTMText(str){
return deprecate(this.stripFormat, 'Client#formatTMText is deprecated, use Client#stripFormat instead')(str);
}
/**
* Format the string and remove the TM style code on it.
* @param {string} str string to format
* @returns {string}
*/
stripFormat(str){
let res, resStr;
// Iterate through the string and check if there are $t,
// First remplace all $T by $t and $Z by $z (for the regex)
resStr = str.replace(/\$T/g, '$t').replace(/\$Z/g, '$z');
// If there is a $t, it will be replaced by the text in uppercase until the $z or the end of the string
while ((res = resStr.match(/\$t(.)*(\$z)|\$t(.)*$/g)) !== null) {
for (let i = 0; i < res.length; i++) {
resStr = resStr.replace(res[i], res[i].toUpperCase());
}
}
return resStr.replace(/\$((\$)|[0-9a-f]{2,3}|[lh]\[.*?\]|.)/gi, '$2');
}
}
module.exports = Client;
/**
* Emitted for general debugging information.
* @event Client#debug
* @param {string} instance The instance name where the debug is triggered
* @param {string} info The debug information
*/
/**
* Emitted when there is an error when fetching external data (Trackmania.exchange for example).
* @event Client#error
* @param {string} error The error
*/

125
packages/bridge-shared/trackmania.io-f/src/data/MatchmakingDivisions.json

@ -0,0 +1,125 @@
{
"3v3": [
{
"name": "Unranked",
"abbr": "U",
"img": "https://trackmania.io/img/mm/Starter.png"
},
{
"name": "Bronze 1",
"abbr": "B1",
"img": "https://trackmania.io/img/mm/B1.png"
},
{
"name": "Bronze 2",
"abbr": "B2",
"img": "https://trackmania.io/img/mm/B2.png"
},
{
"name": "Bronze 3",
"abbr": "B3",
"img": "https://trackmania.io/img/mm/B3.png"
},
{
"name": "Silver 1",
"abbr": "S1",
"img": "https://trackmania.io/img/mm/S1.png"
},
{
"name": "Silver 2",
"abbr": "S2",
"img": "https://trackmania.io/img/mm/S2.png"
},
{
"name": "Silver 3",
"abbr": "S3",
"img": "https://trackmania.io/img/mm/S3.png"
},
{
"name": "Gold 1",
"abbr": "G1",
"img": "https://trackmania.io/img/mm/G1.png"
},
{
"name": "Gold 2",
"abbr": "G2",
"img": "https://trackmania.io/img/mm/G2.png"
},
{
"name": "Gold 3",
"abbr": "G3",
"img": "https://trackmania.io/img/mm/G3.png"
},
{
"name": "Master 1",
"abbr": "M1",
"img": "https://trackmania.io/img/mm/M1.png"
},
{
"name": "Master 2",
"abbr": "M2",
"img": "https://trackmania.io/img/mm/M2.png"
},
{
"name": "Master 3",
"abbr": "M3",
"img": "https://trackmania.io/img/mm/M3.png"
},
{
"name": "Trackmaster",
"abbr": "TM",
"img": "https://trackmania.io/img/mm/TM.png"
}
],
"royal": [
null,
{
"name": "Unranked",
"abbr": "U",
"img": {
"crown": null,
"lion": "https://trackmania.io/img/royal/Lion_Unranked.png"
}
},
{
"name": "Bronze",
"abbr": "B",
"img": {
"crown": "https://trackmania.io/img/royal/Crown_Bronze.png",
"lion": "https://trackmania.io/img/royal/Lion_Bronze.png"
}
},
{
"name": "Silver",
"abbr": "S",
"img": {
"crown": "https://trackmania.io/img/royal/Crown_Silver.png",
"lion": "https://trackmania.io/img/royal/Lion_Silver.png"
}
},
{
"name": "Gold",
"abbr": "G",
"img": {
"crown": "https://trackmania.io/img/royal/Crown_Gold.png",
"lion": "https://trackmania.io/img/royal/Lion_Gold.png"
}
},
{
"name": "Master",
"abbr": "M",
"img": {
"crown": "https://trackmania.io/img/royal/Crown_Master.png",
"lion": "https://trackmania.io/img/royal/Lion_Master.png"
}
},
{
"name": "Trackmaster",
"abbr": "TM",
"img": {
"crown": "https://trackmania.io/img/royal/Crown_RoyalMaster.png",
"lion": "https://trackmania.io/img/royal/Lion_RoyalMaster.png"
}
}
]
}

42
packages/bridge-shared/trackmania.io-f/src/data/PlayerEchelons.json

@ -0,0 +1,42 @@
[
{
"name": "No echelon",
"img": "https://trackmania.io/img/echelon/0.png"
},
{
"name": "Bronze 1",
"img": "https://trackmania.io/img/echelon/1.png"
},
{
"name": "Bronze 2",
"img": "https://trackmania.io/img/echelon/2.png"
},
{
"name": "Bronze 3",
"img": "https://trackmania.io/img/echelon/3.png"
},
{
"name": "Silver 1",
"img": "https://trackmania.io/img/echelon/4.png"
},
{
"name": "Silver 2",
"img": "https://trackmania.io/img/echelon/5.png"
},
{
"name": "Silver 3",
"img": "https://trackmania.io/img/echelon/6.png"
},
{
"name": "Gold 1",
"img": "https://trackmania.io/img/echelon/7.png"
},
{
"name": "Gold 2",
"img": "https://trackmania.io/img/echelon/8.png"
},
{
"name": "Gold 3",
"img": "https://trackmania.io/img/echelon/9.png"
}
]

82
packages/bridge-shared/trackmania.io-f/src/data/RoyalTeams.json

@ -0,0 +1,82 @@
[
{
"name": "Flamingo",
"img": "https://trackmania.io/img/clans/flamingo.png"
},
{
"name": "Pig",
"img": "https://trackmania.io/img/clans/pig.png"
},
{
"name": "Clown Fish",
"img": "https://trackmania.io/img/clans/clown_fish.png"
},
{
"name": "Fox",
"img": "https://trackmania.io/img/clans/fox.png"
},
{
"name": "Octopus",
"img": "https://trackmania.io/img/clans/octopus.png"
},
{
"name": "Butterfly",
"img": "https://trackmania.io/img/clans/butterfly.png"
},
{
"name": "Crocodile",
"img": "https://trackmania.io/img/clans/crocodile.png"
},
{
"name": "Grasshopper",
"img": "https://trackmania.io/img/clans/grasshopper.png"
},
{
"name": "Ladybug",
"img": "https://trackmania.io/img/clans/ladybug.png"
},
{
"name": "Macaw Parrot",
"img": "https://trackmania.io/img/clans/macaw_parrot.png"
},
{
"name": "Giraffe",
"img": "https://trackmania.io/img/clans/giraffe.png"
},
{
"name": "Bee",
"img": "https://trackmania.io/img/clans/bee.png"
},
{
"name": "Dolphin",
"img": "https://trackmania.io/img/clans/dolphin.png"
},
{
"name": "Peafowl",
"img": "https://trackmania.io/img/clans/peafowl.png"
},
{
"name": "Kangaroo",
"img": "https://trackmania.io/img/clans/kangaroo.png"
},
{
"name": "Monkey",
"img": "https://trackmania.io/img/clans/monkey.png"
},
{
"name": "Panda",
"img": "https://trackmania.io/img/clans/panda.png"
},
{
"name": "Zebra",
"img": "https://trackmania.io/img/clans/zebra.png"
},
{
"name": "Rabbit",
"img": "https://trackmania.io/img/clans/rabbit.png"
},
{
"name": "Polar Bear",
"img": "https://trackmania.io/img/clans/polar_bear.png"
}
]

5
packages/bridge-shared/trackmania.io-f/src/index.js

@ -0,0 +1,5 @@
module.exports = {
// Entry point for the application.
Client: require("./client/Client")
};

101
packages/bridge-shared/trackmania.io-f/src/managers/AdsManager.js

@ -0,0 +1,101 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const Ad = require('../structures/Ad');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a in-game ads manager (also called Maniapub).
*/
class AdsManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Ad);
}
/**
* Get the in-game Maniapub list
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the ads or not
* @returns {Promise<Array<Ad>>}
*/
async list(cache = this.client.options.cache.enabled){
const ads = this.client.options.api.paths.tmio.tabs.ads;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${ads}`);
const array = [];
if (res.ads.length > 0) { // check all news from the page 0
for (let i = 0; i < res.ads.length; i++) {
let ad = new Ad(this.client, res.ads[i]);
if (cache) {
res.ads[i]._cachedTimestamp = Date.now();
this._cache.set(res.id, ad);
}
array.push(ad);
}
}
return array;
}
/**
* Fetches a Trackmania Maniapub and returns its data.
* @param {string} adUid The Ad UID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the news from cache or not
* @returns {Promise<Ad>} The Maniapub
* @example
* client.ads.get("fcc22a11-0d59-4fef-b102-0bf5a2df7221").then(ad => {
* console.log(ad.name);
* });
*/
async get(adUid, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(adUid)) {
return this._cache.get(adUid);
} else {
return await this._fetch(adUid, cache);
}
}
/**
* Fetches a maniapub and returns its data
* @param {number} adUid The ad ID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Splashscreen>} The splashscreen
* @private
*/
async _fetch(adUid, cache = this.client.options.cache.enabled){
const ads = this.client.options.api.paths.tmio.tabs.ads;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${ads}`);
if (cache) {
if (res.ads.length > 0) {
for (let i = 0; i < res.ads.length; i++) {
res.ads[i]._cachedTimestamp = Date.now();
this._cache.set(res.ads[i].uid, new Ad(this.client, res.ads[i]));
}
}
if (this._cache.has(adUid)) return this._cache.get(adUid);
else return null;
} else {
if (res.ads.length > 0) { // check all news from the page 0
for (let i = 0; i < res.ads.length; i++) {
if (res.ads[i].uid === adUid) {
return new Ad(this.client, res.ads[i]);
}
}
}
// If we reach this point, the ad was not found
return null;
}
}
}
module.exports = AdsManager;

200
packages/bridge-shared/trackmania.io-f/src/managers/COTDManager.js

@ -0,0 +1,200 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const COTD = require('../structures/COTD');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const Player = require('../structures/Player'); // eslint-disable-line no-unused-vars
const { COTDLeaderboardSortGroup } = require('../util/Constants'); // eslint-disable-line no-unused-vars
/**
* Represents a COTD Manager.
*/
class COTDManager{
constructor(client){
/**
* The client instance.
* @type {Client}
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, COTD);
}
/**
* Get the COTD leaderboard by category
* @param {COTDLeaderboardSortGroup} [sort="wins"] The leaderboard sorting
* @param {boolean} [includeReruns=false] Whether to include reruns when sorting or not
* @param {number} [page=0] The page number
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the list from cache or not
* @returns {Promise<Array<COTDLeaderboard>>}
*/
async leaderboard(sort = "wins", includeReruns = false, page = 0, cache = this.client.options.cache.enabled) {
const cacheKey = `leaderboard_${sort}${includeReruns ? 'reruns' : ''}_${page}`;
if (cache && this._cache.has(cacheKey)) {
return this._cache.get(cacheKey);
} else {
const cotd = this.client.options.api.paths.tmio.tabs.cotd,
players = this.client.options.api.paths.tmio.tabs.players,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${cotd}/${players}/${page}/${sort}${includeReruns ? 'reruns' : ''}`);
let results = [];
for (var i = 0; i < res.players.length; i++) {
results.push(new COTDLeaderboard(this.client, res.players[i]));
}
if (cache) this._cache.set(cacheKey, results);
return results;
}
}
/**
* Fetches the latest COTDs and returns its data
* @param {number} [page=0] The page, each page contains 12 items
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the list from cache or not
* @returns {Promise<Array<COTD>>} The COTD list
* @example
* client.cotd.get().then(event => {
* console.log(event.name);
* });
*/
async get(page = 0, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(page)) {
return this._cache.get(page);
} else {
return await this._fetch(page, cache);
}
}
/**
* Fetches a COTD and returns its data
* @param {number} [page=0] The page
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the list or not
* @returns {Promise<Array<COTD>>} The COTD list
* @private
*/
async _fetch(page = 0, cache = this.client.options.cache.enabled){
const cotd = this.client.options.api.paths.tmio.tabs.cotd;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${cotd}/${page}`);
let arr = [];
res["competitions"].forEach(cotd => {
if (cache) cotd._cachedTimestamp = Date.now();
arr.push(new COTD(this.client, cotd));
});
if (cache) this._cache.set(page, arr);
return arr;
}
}
/**
* Represents a position in the COTD Leaderboard
*/
class COTDLeaderboard {
constructor(client, data) {
/**
* The client instance.
* @type {Client}
*/
this.client = client;
/**
* The data of the COTD leaderboard.
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The player's name
* @type {string}
*/
get playerName() {
return this._data.player.name;
}
/**
* The player's club tag
* @type {string}
*/
get playerTag() {
return this._data.player.tag;
}
/**
* The player's account ID
* @type {string}
*/
get playerId() {
return this._data.player.id;
}
/**
* The player
* @returns {Promise<Player>}
*/
async player() {
return await this.client.players.get(this.playerId);
}
/**
* The position of the player in the selected category
* @type {number}
*/
get position() {
return this._data.position;
}
/**
* The amount of COTD the player has played
* @type {number}
*/
get played() {
return this._data.totalplayed;
}
/**
* The amount of COTD reruns the player has played
* @type {number}
*/
get rerunsPlayed() {
return this._data.totalplayedreruns;
}
/**
* The amount of COTD the player has won
* @type {number}
*/
get wins() {
return this._data.wins;
}
/**
* The amount of COTD reruns the player has won
* @type {number}
*/
get rerunsWins() {
return this._data.winsreruns;
}
/**
* The amount of win streak the player has
* @type {number}
*/
get winStreak() {
return this._data.winstreak;
}
/**
* The amount of win streak the player has (reruns included)
* @type {number}
*/
get winStreakWithReruns() {
return this._data.winstreakreruns;
}
}
module.exports = COTDManager;

62
packages/bridge-shared/trackmania.io-f/src/managers/CacheManager.js

@ -0,0 +1,62 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* The Cache Manager is responsible for managing the cache.
* @extends {Map}
* @private
*/
class CacheManager extends Map {
constructor(client, from, to) {
super();
/**
* The class that instantiated this manager.
* @type {*}
* @readonly
*/
this.from = from;
/**
* The class this manager will operate on.
* @type {*}
* @readonly
*/
this.to = to;
/**
* The client instance.
* @type {Client}
*/
this.client = client;
const ttlOpts = {
default: this.client.options.cache.ttl * 60 * 1000,
Leaderboard: this.client.options.cache.leaderboardttl * 60 * 1000,
Room: this.client.options.cache.roomttl * 60 * 1000
};
/**
* The time to live for the cache in miliseconds.
* @type {number}
* @private
*/
this._ttl = this.client.options.cache.ttl != 10 ? ttlOpts.default : ttlOpts[this.to.name] || ttlOpts.default;
this.client.emit('debug', this.constructor.name, 'Cache is set to a TTL of ' + (this._ttl / 60 / 1000) + ' min for the manager "' + this.from.constructor.name + '" to the class "' + this.to.name + '"');
this._reset();
}
/**
* Resets the cache based on the ttl.
* @private
* @type {void}
*/
_reset() {
setInterval(() => {
this.clear();
this.client.emit('debug', this.constructor.name, 'Cache reset for ' + this.to.name);
}, this._ttl);
}
}
module.exports = CacheManager;

204
packages/bridge-shared/trackmania.io-f/src/managers/CampaignManager.js

@ -0,0 +1,204 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const Campaign = require('../structures/Campaign');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a manager for campaigns.
*/
class CampaignManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Campaign);
}
/**
* Get the current official campaign
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to use from the cache or not
* @returns {Promise<Campaign>} The campaign
*/
async currentSeason(cache = this.client.options.cache.enabled){
const campaigns = this.client.options.api.paths.tmio.tabs.campaigns,
searchRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${campaigns}/0`),
campaignId = searchRes.campaigns[0].id;
if (cache && this._cache.has(campaignId)){
return this._cache.get(campaignId);
} else {
return await this._fetch(0, campaignId, cache);
}
}
/**
* Get all official campaigns from recent to old
* @returns {Promise<Array<CampaignSearchResult>>} The campaigns
*/
async officialCampaigns(){
const campaigns = this.client.options.api.paths.tmio.tabs.campaigns,
searchRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${campaigns}/0`);
let arr = [];
for (const campaign of searchRes.campaigns) {
if (campaign.clubid != 0) break;
arr.push(new CampaignSearchResult(this.client, campaign));
}
return arr;
}
/**
* Get all popular campaigns (official excluded) (50 items / page)
* @param {number} [page=0] The page number
* @returns {Promise<Array<CampaignSearchResult>>} The campaigns
*/
async popularCampaigns(page = 0){
const campaigns = this.client.options.api.paths.tmio.tabs.campaigns,
searchRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${campaigns}/${page}`);
let arr = [];
for (const campaign of searchRes.campaigns) {
if (campaign.clubid == 0) continue;
arr.push(new CampaignSearchResult(this.client, campaign));
}
return arr;
}
/**
* Searches for a campaign
* @param {string} query The query
* @param {number} [page=0] The page number
* @returns {Promise<Array<CampaignSearchResult>>} The campaigns
* @example
* client.campaigns.search('htimh').then(campaigns => {
* campaigns[0].getCampaign().then(async campaign => {
* const maps = await campaign.maps();
* maps.forEach(map => console.log(map.name));
* });
* });
*/
async search(query, page = 0){
const campaigns = this.client.options.api.paths.tmio.tabs.campaigns,
searchRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${campaigns}/${page}?search=${query}`);
let arr = [];
for (const campaign of searchRes.campaigns) {
arr.push(new CampaignSearchResult(this.client, campaign));
}
return arr;
}
/**
* Fetches a Trackmania campaign and returns its data
* @param {number} clubId The club Id that the campaign belongs to (If it's an official campaign, set it to 0)
* @param {number} id The campaign Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the campaign from cache or not
* @returns {Promise<Campaign>} The campaign
* @example
* client.campaigns.get(54, 10621).then(campaign => {
* console.log(campaign.name);
* });
*/
async get(clubId, id, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(id)) {
return this._cache.get(id);
} else {
return await this._fetch(clubId, id, cache);
}
}
/**
* Fetches a campaign and returns its data
* @param {number} clubId The club Id that the campaign belongs to
* @param {string} id The campaign Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the campaign or not
* @returns {Campaign} The campaign
* @private
*/
async _fetch(clubId, id, cache = this.client.options.cache.enabled){
let campaign, res;
if (clubId == 0) {
// Official campaign
campaign = this.client.options.api.paths.tmio.tabs.officialCampaign;
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${campaign}/${id}`);
} else {
// Club campaign
campaign = this.client.options.api.paths.tmio.tabs.campaign;
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${campaign}/${clubId}/${id}`);
}
const theCampaign = new Campaign(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.id, theCampaign);
}
return theCampaign;
}
}
/**
* The result of a campaign search. It is completely different from the {@link Campaign} object.
*/
class CampaignSearchResult {
constructor(client, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The campaign's ID
* @type {number}
*/
this.id = data.id;
/**
* The campaign's Club ID
* @type {number}
*/
this.clubId = data.clubid;
/**
* The campaign's name
* @type {string}
*/
this.name = data.name;
/**
* The campaign's creation date
* @type {Date}
*/
this.date = new Date(data.timestamp * 1000);
/**
* The campaign's map count
* @type {number}
*/
this.mapCount = data.mapcount;
}
/**
* Return to the Campaign Object
* @returns {Promise<Campaign>}
*/
async getCampaign(){
const campaign = await this.client.campaigns.get(this.clubId, this.id);
return campaign;
}
}
module.exports = CampaignManager;

101
packages/bridge-shared/trackmania.io-f/src/managers/ClubManager.js

@ -0,0 +1,101 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const Club = require('../structures/Club');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a manager for clubs.
*/
class ClubManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Club);
}
/**
* Gets all the popular clubs
* @param {number} [page=0] The page number
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the clubs or not
* @returns {Promise<Array<Club>>} The clubs
*/
async popularClubs(page = 0, cache = this.client.options.cache.enabled){
const clubs = this.client.options.api.paths.tmio.tabs.clubs,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${clubs}/${page}?sort=popularity`),
clubsList = res.clubs.map(club => new Club(this.client, club));
if (cache) {
for (const club of clubsList) {
club._cachedTimestamp = Date.now();
this._cache.set(club.id, club);
}
}
return clubsList;
}
/**
* Searches for a club
* @param {string} query Search query
* @param {number} [page=0] The page number
* @returns {Promise<Array<Club>>}
*/
async search(query, page = 0){
const clubs = this.client.options.api.paths.tmio.tabs.clubs,
searchRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${clubs}/${page}?search=${query}`);
let arr = [];
for (const club of searchRes.clubs) {
arr.push(new Club(this.client, club));
}
return arr;
}
/**
* Fetches a Trackmania Club and returns its data
* @param {number} id The Club Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the club from cache or not
* @returns {Promise<Club>} The Club
* @example
* client.clubs.get(54).then(club => {
* console.log(club.name);
* });
*/
async get(id, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(id)) {
return this._cache.get(id);
} else {
return await this._fetch(id, cache);
}
}
/**
* Fetches a map and returns its data
* @param {string} id The Club Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the club or not
* @returns {Club} The club
* @private
*/
async _fetch(id, cache = this.client.options.cache.enabled){
const club = this.client.options.api.paths.tmio.tabs.club;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${club}/${id}`);
const theClub = new Club(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.id, theClub);
}
return theClub;
}
}
module.exports = ClubManager;

142
packages/bridge-shared/trackmania.io-f/src/managers/EventManager.js

@ -0,0 +1,142 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const TMEvent = require('../structures/TMEvent');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a manager for in-game events.
*/
class EventManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, TMEvent);
}
/**
* List all available events by creation date
* @param {number} [page=0] The page number
* @returns {Promise<Array<EventSearchResult>>} The events
*/
async listEvents(page = 0){
const events = this.client.options.api.paths.tmio.tabs.events,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${events}/${page}`),
eventList = res.competitions.map(event=> new EventSearchResult(this.client, event));
return eventList;
}
/**
* Searches for an event by name
* @param {string} query The query
* @param {number} [page=0] The page number
* @returns {Promise<Array<EventSearchResult>>} The events
*/
async search(query, page = 0){
const events = this.client.options.api.paths.tmio.tabs.events,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${events}/${page}?search=${query}`),
eventList = res.competitions.map(event=> new EventSearchResult(this.client, event));
return eventList;
}
/**
* Fetches a Trackmania event and returns its data
* @param {number} eventId The event id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the map from cache or not
* @returns {Promise<TMEvent>} The event
* @example
* client.events.get(706).then(event => {
* console.log(event.name);
* });
*/
async get(eventId, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(eventId)) {
return this._cache.get(eventId);
} else {
return await this._fetch(eventId, cache);
}
}
/**
* Fetches a event and returns its data
* @param {number} eventId The event id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the map or not
* @returns {Event} The event
* @private
*/
async _fetch(eventId, cache = this.client.options.cache.enabled){
const comp = this.client.options.api.paths.tmio.tabs.comp;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${comp}/${eventId}`);
const theEvent = new TMEvent(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.id, theEvent);
}
return theEvent;
}
}
/**
* The result of a campaign search. It is completely different from the {@link TMEvent} object.
*/
class EventSearchResult {
constructor(client, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The event's ID
* @type {number}
*/
this.id = data.id;
/**
* The event's competiton ID
* @type {number}
*/
this.compId = data.id;
/**
* The event's Club ID
* @type {number}
*/
this.clubId = data.clubid;
/**
* The event's name
* @type {string}
*/
this.name = data.name;
/**
* The event's creation date
* @type {Date}
*/
this.date = new Date(data.timestamp * 1000);
}
/**
* Return to the TMEvent Object
* @returns {Promise<TMEvent>}
*/
async event(){
return await this.client.events.get(this.id);
}
}
module.exports = EventManager;

65
packages/bridge-shared/trackmania.io-f/src/managers/MapManager.js

@ -0,0 +1,65 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const TMMap = require('../structures/TMMap');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a map manager.
*/
class MapManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, TMMap);
}
/**
* Fetches a Trackmania map and returns its data
* @param {string} mapUid The map UID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the map from cache or not
* @returns {Promise<TMMap>} The map
* @example
* client.maps.get('z28QXoFnpODEGgg8MOederEVl3j').then(map => {
* console.log(map.name);
* });
*/
async get(mapUid, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(mapUid)) {
return this._cache.get(mapUid);
} else {
return await this._fetch(mapUid, cache);
}
}
/**
* Fetches a map and returns its data
* @param {string} mapUid The map UID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the map or not
* @returns {TMMap} The map
* @private
*/
async _fetch(mapUid, cache = this.client.options.cache.enabled){
const map = this.client.options.api.paths.tmio.tabs.map;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${map}/${mapUid}`);
const theMap = new TMMap(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.mapUid, theMap);
}
return theMap;
}
}
module.exports = MapManager;

132
packages/bridge-shared/trackmania.io-f/src/managers/MatchesManager.js

@ -0,0 +1,132 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const {MMTypes, MatchmakingGroup, MatchStatus} = require('../util/Constants'); // eslint-disable-line no-unused-vars
const Match = require('../structures/Match');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents the matches manager (3v3 or Royal matches).
*/
class MatchesManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Match);
}
/**
* Get a list of the matches
* @param {MatchmakingGroup} [group='3v3'] The group to get the matches from
* @param {number} [page=0] The page to get
* @param {Boolean} [cache=this.client.options.cache.enabled] Whether to cache the matches or not
* @returns {Promise<Array<MatchResult>>}
*/
async list(group = 2, page = 0, cache = this.client.options.cache.enabled){
if (typeof group == 'string') group = MMTypes[group];
const matches = this.client.options.api.paths.tmio.tabs.matches;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${matches}/${group}/${page}`);
const array = [];
if (res.matches.length > 0) {
for (let i = 0; i < res.matches.length; i++) {
let match = new MatchResult(this.client, res.matches[i]);
if (cache) {
res.matches[i]._cachedTimestamp = Date.now();
this._cache.set(res.id, match);
}
array.push(match);
}
}
return array;
}
/**
* Fetches a Trackmania Match and returns its data.
* @param {string} liveID The live ID of the match (should start with 'LID-MTCH')
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the news from cache or not
* @returns {Promise<Match>} The Match
*/
async get(liveID, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(liveID)) {
return this._cache.get(liveID);
} else {
return await this._fetch(liveID, cache);
}
}
/**
* Fetches a Match and returns its data
* @param {string} liveID Thelive ID of the match (should start with 'LID-MTCH')
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Match>} The splashscreen
* @private
*/
async _fetch(liveID, cache = this.client.options.cache.enabled){
if (!liveID.startsWith("LID-MTCH")) throw "The live ID must start with 'LID-MTCH'";
const match = this.client.options.api.paths.tmio.tabs.match;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${match}/${liveID}`);
const theMatch = new Match(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.lid, theMatch);
}
return theMatch;
}
}
/**
* The result of a Match from the list. It is completely different from the {@link Match} object.
*/
class MatchResult {
constructor(client, data){
/**
* The client instance.
* @type {Client}
*/
this.client = client;
/**
* The match ID
* @type {number}
*/
this.id = data.id;
/**
* The match Live ID
* @type {string}
*/
this.liveID = data.lid;
/**
* The match start date
* @type {Date}
*/
this.startDate = new Date(data.starttime);
/**
* The status of the match
* @type {MatchStatus}
*/
this.status = data.status;
}
/**
* Get the match
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the match or not
*/
async match(cache = this.client.options.cache.enabled){
return this.client.matches.get(this.liveID, cache);
}
}
module.exports = MatchesManager;

131
packages/bridge-shared/trackmania.io-f/src/managers/NewsManager.js

@ -0,0 +1,131 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const Splashscreen = require('../structures/Splashscreen');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a in-game news manager.
*/
class NewsManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Splashscreen);
}
/**
* Get the in-game news list
* @param {number} page The page number
* @param {Boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Array<Splashscreen>>}
*/
async list(page = 0, cache = this.client.options.cache.enabled){
const news = this.client.options.api.paths.tmio.tabs.news;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${news}/${page}`);
const array = [];
if (res.splashscreens.length > 0) { // check all news from the page 0
for (let i = 0; i < res.splashscreens.length; i++) {
let splashscreen = new Splashscreen(this.client, res.splashscreens[i]);
if (cache) {
res.splashscreens[i]._cachedTimestamp = Date.now();
this._cache.set(res.id, splashscreen);
}
array.push(splashscreen);
}
}
return array;
}
/**
* Fetches a Trackmania splashscreen and returns its data.
* @param {number} newsId The splashscreen ID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the news from cache or not
* @returns {Promise<Splashscreen>} The splashscreen
* @example
* client.news.get(143).then(news => {
* console.log(news.title);
* });
*/
async get(newsId, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(newsId)) {
return this._cache.get(newsId);
} else {
return await this._fetch(newsId, cache);
}
}
/**
* Fetches a splashscreen and returns its data
* @param {number} newsId The splashscreen ID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Splashscreen>} The splashscreen
* @private
*/
async _fetch(newsId, cache = this.client.options.cache.enabled){
const news = this.client.options.api.paths.tmio.tabs.news;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${news}/0`);
if (cache) {
// Get page_max from response, if set to 1, then there is only one page
// else we need to fetch all pages given by page_max
// and cache all splashscreens
if (res.splashscreens.length > 0) {
for (let i = 0; i < res.splashscreens.length; i++) {
res.splashscreens[i]._cachedTimestamp = Date.now();
this._cache.set(res.id, new Splashscreen(this.client, res.splashscreens[i]));
}
}
if (res.page_max > 1) {
for (let i = 1; i < res.page_max; i++) {
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${news}/${i}`);
if (res.splashscreens.length > 0) {
for (let i = 0; i < res.splashscreens.length; i++) {
res.splashscreens[i]._cachedTimestamp = Date.now();
this._cache.set(res.id, new Splashscreen(this.client, res.splashscreens[i]));
}
}
}
}
if (this._cache.has(newsId)) return this._cache.get(newsId);
else return null;
} else {
if (res.splashscreens.length > 0) { // check all news from the page 0
for (let i = 0; i < res.splashscreens.length; i++) {
if (res.splashscreens[i].id === newsId) {
return new Splashscreen(this.client, res.splashscreens[i]);
}
}
}
if (res.page_max > 1) { // check all news from the pages 1 to page_max
for (let i = 1; i < res.page_max; i++) {
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${news}/${i}`);
if (res.splashscreens.length > 0) {
for (let i = 0; i < res.splashscreens.length; i++) {
if (res.splashscreens[i].id === newsId) {
return new Splashscreen(this.client, res.splashscreens[i]);
}
}
}
}
}
// If we reach this point, the news was not found
return null;
}
}
}
module.exports = NewsManager;

304
packages/bridge-shared/trackmania.io-f/src/managers/PlayerManager.js

@ -0,0 +1,304 @@
const Player = require('../structures/Player');
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const {PlayerGroup, MMTypes, MatchmakingGroup} = require('../util/Constants'); // eslint-disable-line no-unused-vars
const MatchmakingDivision = require('../structures/MatchmakingDivision');
/**
* Represents a manager for players.
*/
class PlayerManager {
constructor(client){
/**
* The client instance
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Player);
}
/**
* Returns the login of an account ID
* @param {string} accountID The account ID
* @returns {string}
*/
toLogin(accountID){
var chars = accountID.replace(/-/g, '');
var bytes = '';
for (var i = 0; i < chars.length; i += 2) {
var hex = chars.substr(i, 2);
var dec = parseInt(hex, 16);
var byte = String.fromCharCode(dec);
bytes += byte;
}
return btoa(bytes)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
/**
* Returns the Account ID of a login
* @param {string} login The login of the player
* @returns {?string}
*/
toAccountId(login){
if (login.length != 22) return null;
var bytes = atob(login
.replace(/-/g, '+')
.replace(/_/g, '/')
);
var ret = '';
for (var i = 0; i < bytes.length; i++) {
if (i == 4 || i == 6 || i == 8 || i == 10) {
ret += '-';
}
ret += bytes.charCodeAt(i).toString(16);
}
return ret;
}
/**
* Searches for a player by its name
* @param {string} query The query to search for
* @returns {Promise<Array<PlayerSearchResult>>} The results
* @example
* // Search for a player
* client.players.search('greep').then(results => {
* client.players.get(results[0].id).then(player => {
* console.log('The tag of this player is', player.tag);
* });
* });
*/
async search(query){
const players = this.client.options.api.paths.tmio.tabs.players,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${players}/find?search=${query}`);
let results = [];
for (var i = 0; i < res.length; i++) {
results.push(new PlayerSearchResult(this.client, res[i].player));
}
return results;
}
/**
* Get all players from a group
* @param {PlayerGroup} groupName The group name
* @returns {?Promise<Array<PlayerSearchResult>>} The results
*/
async group(groupName){
const players = this.client.options.api.paths.tmio.tabs.players,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${players}/group/${groupName}`);
if (res.error) {
throw res.error;
}
let results = [];
for (var i = 0; i < res.length; i++) {
results.push(new PlayerSearchResult(this.client, res[i].player));
}
return results;
}
/**
* Get the trophy leaderboard
* @param {number} [page=0] The page number
* @returns {Promise<Array<PlayerTopTrophy>>} The players' top trophies
* @example
* Client.players.topTrophies().then(top => {
* console.log("The number 1 player is " + top[0].player.name + " with " + top[0].score + " trophies");
* });
*/
async topTrophies(page = 0){
const top = this.client.options.api.paths.tmio.tabs.topTrophies,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${top}/${page}`);
if (res.error) throw res.error;
const topsArray = res.ranks.map(playerTop=> new PlayerTopTrophy(this.client, playerTop));
return topsArray;
}
/**
* Gets the matchmaking leaderboard
* @param {MatchmakingGroup} group The matchmaking group
* @param {number} [page=0] The page number
* @returns {Promise<Array<PlayerTopMatchmaking>>} The players' top matchmaking
*/
async topMatchmaking(group, page = 0){
const top = this.client.options.api.paths.tmio.tabs.topMatchmaking,
typeId = typeof group == "string" ? MMTypes[group] : group,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${top}/${typeId}/${page}`);
if (res.error) throw res.error;
const topsArray = res.ranks.map(playerTop=> new PlayerTopMatchmaking(this.client, typeId, playerTop));
return topsArray;
}
/**
* Fetches a player and returns its data
* @param {string} accountId The account ID or its tm.io vanity name
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the player from cache or not
* @returns {Promise<Player>} The player
* @example
* // Get a player
* client.players.get('26d9a7de-4067-4926-9d93-2fe62cd869fc').then(player => {
* console.log(player.name);
* });
*/
async get(accountId, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(accountId)) {
return this._cache.get(accountId);
} else {
return await this._fetch(accountId, cache);
}
}
/**
* Fetches a player and returns its data
* @param {string} accountId The account ID or its tm.io vanity name
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the player or not
* @returns {Player} The player
* @private
*/
async _fetch(accountId, cache = this.client.options.cache.enabled){
const player = this.client.options.api.paths.tmio.tabs.player,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${player}/${accountId}`);
const thePlayer = new Player(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.accountid, thePlayer);
// Adds also the player by its vanity name in the cache
if (res.meta && (res.meta.vanity && res.meta.vanity != "")){
this._cache.set(res.meta.vanity, thePlayer);
}
}
return thePlayer;
}
}
/**
* Represents a player top trophy
*/
class PlayerTopTrophy {
constructor(client, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The player
* @type {PlayerSearchResult}
*/
this.player = new PlayerSearchResult(this.client, data.player);
/**
* The rank
* @type {number}
*/
this.rank = data.rank;
/**
* The score (number of trophies)
* @type {number}
*/
this.score = data.score;
}
}
/**
* The player top matchmaking
*/
class PlayerTopMatchmaking{
constructor(client, typeId, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The player
* @type {PlayerSearchResult}
*/
this.player = new PlayerSearchResult(this.client, data.player);
/**
* The rank
* @type {number}
*/
this.rank = data.rank;
/**
* The score
* @type {number}
*/
this.score = data.score;
/**
* The matchmaking division of the player
* @type {MatchmakingDivision}
*/
this.division = new MatchmakingDivision(client, typeId, data.division);
}
}
/**
* The result of a player search. It is completely different from the {@link Player} object.
*/
class PlayerSearchResult {
constructor(client, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The player's account ID
* @type {string}
*/
this.id = data.id;
/**
* The player's display name
* @type {string}
*/
this.name = data.name;
/**
* The player's club tag (if any)
* @type {?string}
*/
this.tag = data.tag ? data.tag : null;
}
/**
* Return to the Player Object
* @returns {Promise<Player>}
*/
async player(){
return await this.client.players.get(this.id);
}
}
module.exports = PlayerManager;

149
packages/bridge-shared/trackmania.io-f/src/managers/RoomManager.js

@ -0,0 +1,149 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const Room = require('../structures/Room');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a manager for rooms.
*/
class RoomManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, Room);
}
/**
* Get the popular Club rooms (by number of players connected)
* @param {number} [page=0] The page number
* @returns {Promise<Array<RoomSearchResult>>} The rooms
*/
async popularRooms(page = 0){
const rooms = this.client.options.api.paths.tmio.tabs.rooms,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${rooms}/${page}`),
roomList = res.rooms.map(room=> new RoomSearchResult(this.client, room));
return roomList;
}
/**
* Searches for a room
* @param {string} query The query to search for
* @param {number} [page=0] The page number
* @returns {Promise<Array<RoomSearchResult>>} The rooms
*/
async search(query, page = 0){
const rooms = this.client.options.api.paths.tmio.tabs.rooms,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${rooms}/${page}?search=${query}`),
roomList = res.rooms.map(room=> new RoomSearchResult(this.client, room));
return roomList;
}
/**
* Fetches a Trackmania room (server) and returns its data
* @param {number} clubId The club Id that the room belongs to
* @param {number} id The room Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the room from cache or not
* @returns {Promise<Room>} The room
* @example
* client.rooms.get(338, 1180).then(room => {
* console.log(room.name);
* });
*/
async get(clubId, id, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(id)) {
return this._cache.get(id);
} else {
return await this._fetch(clubId, id, cache);
}
}
/**
* Fetches a room and returns its data
* @param {number} clubId The club Id that the room belongs to
* @param {string} id The room Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the room or not
* @returns {Campaign} The room
* @private
*/
async _fetch(clubId, id, cache = this.client.options.cache.enabled){
const room = this.client.options.api.paths.tmio.tabs.room,
res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${room}/${clubId}/${id}`),
theRoom = new Room(this.client, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(res.id, theRoom);
}
return theRoom;
}
}
/**
* The result of a campaign search. It is completely different from the {@link Room} object.
*/
class RoomSearchResult {
constructor(client, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The room's ID
* @type {number}
*/
this.id = data.id;
/**
* The room's Club ID
* @type {number}
*/
this.clubId = data.clubid;
/**
* The room's name
* @type {string}
*/
this.name = data.name;
/**
* Whether the room is hosted by Nadeo
* @type {boolean}
*/
this.nadeoHosted = data.nadeo;
/**
* The player count
* @type {number}
*/
this.playerCount = data.playercount;
/**
* The max player count
* @type {number}
*/
this.maxPlayerCount = data.playermax;
}
/**
* Return to the Room Object
* @returns {Promise<Room>}
*/
async room(){
return await this.client.rooms.get(this.clubId, this.id);
}
}
module.exports = RoomManager;

99
packages/bridge-shared/trackmania.io-f/src/managers/TOTDManager.js

@ -0,0 +1,99 @@
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('./CacheManager');
const TOTD = require('../structures/TOTD');
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a manager for TOTDs.
*/
class TOTDManager{
constructor(client){
/**
* The client instance.
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
this._cache = new CacheManager(this.client, this, TOTD);
}
/**
* Calculate the number of months between today and the month and year
* @param {Date} date The date
* @private
*/
_calculateMonths(date){
const today = new Date();
const todayMonth = today.getMonth();
const todayYear = today.getFullYear();
const months = (date.getFullYear() - todayYear) * 12;
return (months + date.getMonth() - todayMonth) * -1;
}
/**
* Fetches a TOTD with it's day and returns its data
* @param {Date} date The date
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the map from cache or not
* @returns {Promise<TOTD>} The map
* @example
* // Gets the TOTD of today
* client.totd.get(new Date()).then(totd => {
* console.log(totd.map.name);
* });
*/
async get(date, cache = this.client.options.cache.enabled){
if (cache && this._cache.has(date.getMonth()+"_"+date.getFullYear())) {
let month = this._cache.get(date.getMonth()+"_"+date.getFullYear()),
dayMap = month.days.find(map => map.monthday == date.getDate());
if (!dayMap) dayMap = month.days.find(map => map.monthday == date.getDate()-1);
if (!dayMap) throw "Track of the day not found, it is the right date?";
return new TOTD(this.client, dayMap);
} else {
return await this._fetch(date, cache);
}
}
/**
* Fetches a TOTD and returns its data
* @param {Date} date The date
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the map or not
* @returns {TOTD} The map
* @private
*/
async _fetch(date, cache = this.client.options.cache.enabled){
const totd = this.client.options.api.paths.tmio.tabs.totd;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${totd}/${this._calculateMonths(date)}`);
if (res.year != date.getFullYear() && res.month != date.getMonth()+1) {
throw "Invalid date";
}
if (cache) {
res._cachedTimestamp = Date.now();
this._cache.set(date.getMonth()+"_"+date.getFullYear(), res);
}
//get the map of the day in the month on the res
let dayMap = res.days.find(map => map.monthday == date.getDate());
if (!dayMap) dayMap = res.days.find(map => map.monthday == date.getDate()-1);
if (!dayMap) throw "Track of the day not found, it is the right date?";
dayMap.month = res.month;
dayMap.year = res.year;
return new TOTD(this.client, dayMap);
}
}
module.exports = TOTDManager;

124
packages/bridge-shared/trackmania.io-f/src/rest/APIRequest.js

@ -0,0 +1,124 @@
const fetch = require('node-fetch');
const pkg = require('../../package.json');
const ReqUtil = require('../util/ReqUtil');
const BaseClient = require('../client/BaseClient'); // eslint-disable-line no-unused-vars
/**
* APIRequest class
* @private
*/
class APIRequest {
/**
* @param {BaseClient} client The client object
*/
constructor(client) {
/**
* The client that created this request
* @type {BaseClient}
* @readonly
*/
this.client = client;
// Creating UA string
let cwd = process.cwd(),
cwf;
cwd = cwd.substring(cwd.lastIndexOf(require('os').type == 'Windows_NT' ? '\\' : '/')+1);
// Gets the current file name where the script starts
// If require.main is undefined, that means it executes that in the CLI
if (require.main){
cwf = require.main.filename;
cwf = cwf.substring(cwf.lastIndexOf(require('os').type == 'Windows_NT' ? '\\' : '/')+1);
}
else cwf = "Node.js cli";
/**
* The User-Agent string which is used for the requests
* @type {string}
*/
this.UA = this.client.options.api.useragent;
/**
* The API key for the requests on trackmania.io
* @type {string}
*/
this.key = this.client.options.api.key;
if (this.UA != null || !cwd.includes(pkg.name)) {
if (this.UA == null) this.UA = cwd + ' (' + cwf + ')' ;
else this.UA += ' (' + cwf + ')';
this.UA += ' - using';
} else this.UA = '[TESTING BUILD]';
this.UA += ' node-' + pkg.name + ' ' + pkg.version;
}
/**
* Makes a request to the API
* @param {string} url The URL
* @param {string} [method="GET"] The method
* @param {Object} [body=null] The body
* @returns {Promise<Object>} The response
*/
do(url, method = 'GET', body = null) {
this.url = url;
// Creating options
const headers = new fetch.Headers({
"Accept" : "application/json",
"Content-Type" : "application/json",
"User-Agent" : this.UA
});
if (this.url.startsWith(new ReqUtil(this.client).tmioAPIURL) && this.key) headers.append('X-API-Key', this.key);
this.options = {
headers,
method,
body
};
/**
* Emitted before every API request.
* This event can emit several times for the same request, e.g. when hitting a rate limit.
* <info>This is an informational event that is emitted quite frequently,
* it is highly recommended to check `request.url` to filter the data.</info>
* @event Client#apiRequest
* @param {APIRequest} request The request that is about to be sent
*/
this.client.emit('apiRequest', this);
return fetch(this.url, this.options)
.then(async response => {
/**
* Emitted after every API request has received a response.
* This event does not necessarily correlate to completion of the request, e.g. when hitting a rate limit.
* <info>This is an informational event that is emitted quite frequently,
* it is highly recommended to check `request.url` to filter the data.</info>
* @event Client#apiResponse
* @param {APIRequest} request The request that triggered this response
* @param {Response} response The response received from the API
*/
this.client.emit('apiResponse', this, response);
if (response.ok) {
// Save the rate limit details
if (this.url.startsWith(new ReqUtil(this.client).tmioAPIURL)){
this.client.ratelimit = {
ratelimit: Number(response.headers.raw()['x-ratelimit-limit'][0]),
remaining: Number(response.headers.raw()['x-ratelimit-remaining'][0]),
reset: new Date(Number(response.headers.raw()['x-ratelimit-reset'][0]) * 1000)
};
}
return await response.json();
} else {
const json = await response.json();
if (json) {
if (json.error) throw json.error;
else throw json;
} else throw response.statusText;
}
})
.catch(error => {
if (this.client.options.dev) error = error + " ("+this.url+")";
throw error;
});
}
}
module.exports = APIRequest;

96
packages/bridge-shared/trackmania.io-f/src/structures/Ad.js

@ -0,0 +1,96 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const {AdType} = require('../util/Constants'); // eslint-disable-line no-unused-vars
/**
* The Maniapub
*/
class Ad {
constructor(client, data){
/**
* The client instance.
* @type {Client}
*/
this.client = client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The Ad UID
* @type {string}
*/
get uid(){
return this._data.uid;
}
/**
* The name of the Ad
* @type {string}
*/
get name(){
return this._data.name;
}
/**
* The type of the Ad
* @type {AdType}
*/
get type(){
return this._data.type;
}
/**
* The URL linked with this ad
* @type {string}
*/
get url(){
return this._data.url;
}
/**
* The 2x3 image URL of this ad (shown on the vertical screen)
* @type {string}
*/
get verticalImage(){
return this._data.img2x3;
}
/**
* The 16x9 image URL of this ad (shown on the big screen)
* @type {string}
*/
get image(){
return this._data.img16x9;
}
/**
* The 64x10 image URL of this ad (shown on Start, CPs and Finish)
* @type {string}
*/
get cpImage(){
return this._data.img64x10;
}
/**
* The media of this ad (mostly the vertical image)
* @type {string}
*/
get media(){
return this._data.media;
}
/**
* The display format of this screen (2x3 vertical image by default)
* @type {string}
*/
get displayFormat(){
return this._data.displayformat;
}
}
module.exports = Ad;

71
packages/bridge-shared/trackmania.io-f/src/structures/COTD.js

@ -0,0 +1,71 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const TMEvent = require('./TMEvent'); // eslint-disable-line no-unused-vars
/**
* Represents a COTD event.
*/
class COTD {
constructor(client, data){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The event associated on this COTD
* @returns {Promise<TMEvent>}
*/
async getEvent(){
return this.client.events.get(this._data.id);
}
/**
* The COTD identifier
* @type {number}
*/
get id(){
return this._data.id;
}
/**
* The COTD name
* @type {string}
*/
get name(){
return this._data.name;
}
/**
* The number of players in this COTD
* @type {number}
*/
get playerCount(){
return this._data.players;
}
/**
* The start date of this COTD
* @type {Date}
*/
get startDate(){
return new Date(this._data.starttime * 1000);
}
/**
* The end date of this COTD
* @type {Date}
*/
get endDate(){
return new Date(this._data.endtime * 1000);
}
}
module.exports = COTD;

455
packages/bridge-shared/trackmania.io-f/src/structures/Campaign.js

@ -0,0 +1,455 @@
const Player = require('./Player'); // eslint-disable-line no-unused-vars
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const TMMap = require('./TMMap'); // eslint-disable-line no-unused-vars
const Club = require('./Club'); // eslint-disable-line no-unused-vars
const ReqUtil = require('../util/ReqUtil');
const EventEmitter = require('events');
/**
* The Campaign class represents a campaign.
*/
class Campaign extends EventEmitter {
constructor(client, data) {
super();
/**
* The client object of the campaign.
* @type {Client}
*/
this.client = client;
/**
* The data object
* @type {Object}
* @private
*/
this._data = data;
/**
* The leaderboard of the campaign
* @type {Array<CampaignLeaderboard>}
*/
this.leaderboard = [];
}
/**
* The id of the campaign.
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The name of the campaign.
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* Whether the campaign is official.
* @type {boolean}
*/
get isOfficial() {
return this._data.clubid === 0;
}
/**
* The image URL of the campaign. If this is an official campaign, the decal image URL is returned.
* @type {string}
*/
get image() {
if (this.isOfficial) {
const ReqUtil = require('../util/ReqUtil');
return new ReqUtil(this.client).tmioURL + this._data.mediae.decal;
} else return this._data.media;
}
/**
* The creation date of the campaign.
* @type {Date}
*/
get createdAt() {
return new Date(this._data.creationtime*1000);
}
/**
* The last update date of the campaign.
* @type {Date}
*/
get updatedAt() {
return new Date(this._data.lastupdatetime*1000);
}
/**
* The club that owns the campaign. (if it's not an official campaign)
* @returns {?Promise<Club>}
*/
async club() {
if (this.isOfficial) return null;
else return this.client.clubs.get(this._data.clubid);
}
/**
* The leaderboard id of the campaign.
* @type {string}
*/
get leaderboardId() {
return this._data.leaderboarduid;
}
/**
* Get the number of maps in the campaign.
* @type {number}
*/
get mapCount() {
return this._data.playlist.length;
}
/**
* Get a specific map of the campaign.
* @param {number} index The index of the map.
* @returns {Promise<TMMap>}
*/
async map(index) {
return await this.client.maps.get(this._data.playlist[index].mapUid);
}
/**
* The list of maps in the campaign.
* @returns {Promise<Array<TMMap>>}
* @example
* Client.campaigns.get(0, 11612).then(async campaign => {
* const maps = await campaign.maps();
* maps.forEach(map => console.log(map.name));
* });
*/
async maps() {
const array = [];
for (let i = 0; i < this._data.playlist.length; i++) {
let map = await this.client.maps.get(this._data.playlist[i].mapUid);
array.push(map);
}
return array;
}
/**
* Load more results in the leaderboard.
* @param {number} [nbOfResults=100] The number of results to load. (max 100)
* @returns {Promise<?Array<CampaignLeaderboard>>}
*/
async leaderboardLoadMore(nbOfResults = 100) {
if (nbOfResults > 100) nbOfResults = 100;
if (nbOfResults < 1) nbOfResults = 1;
const leaderboard = this.client.options.api.paths.tmio.tabs.leaderboard,
params = new URLSearchParams();
params.append('offset', this.leaderboard.length);
params.append('length', nbOfResults);
const leaderboardRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${leaderboard}/${this.leaderboardId}?${params.toString()}`);
if (leaderboardRes.tops != null){
for (let i = 0; i < leaderboardRes.tops.length; i++) {
this.leaderboard.push(new CampaignLeaderboard(this, leaderboardRes.tops[i]));
}
}
return this.leaderboard;
}
/**
* The media images of the campaign, if this is an official campaign.
* @type {?CampaignMedia}
*/
get media() {
if (this.isOfficial) {
return new CampaignMedia(this.client, this._data.mediae);
} else return null;
}
/**
* Whether the campaign is tracked.
* @type {boolean}
*/
get isTracked() {
return this._data.tracked;
}
/**
* Gets the campaign activity.
* <info>{@link Campaign#isTracked} must be true.</info>
* @returns {Promise<Array<CampaignRecordActivity>>}
*/
async activity(page = 0) {
if (!this.isTracked) throw 'Campaign is not tracked.';
const activity = this.client.options.api.paths.tmio.tabs.activity,
leaderboard = this.client.options.api.paths.tmio.tabs.leaderboard,
activityRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${leaderboard}/${activity}/${this.leaderboardId}/${page}`);
return activityRes.map(activity => new CampaignRecordActivity(this, activity));
}
/**
* Subscribe to the campaign WR updates.
* <info>{@link Campaign#isTracked} must be true.</info>
* <info>When a new WR is set, the event {@link Campaign#e-wr} will be fired</info>
* @returns {Promise<void>}
* @example
* Client.campaigns.currentSeason().then(campaign => {
* campaign.subWR();
* campaign.on('wr', (map, record) => {
* console.log(`New WR on ${campaign.name} in ${map.name} is ${record.playerName} (${record.time})`);
* });
* });
*/
async subWR() {
if (!this.isTracked) throw 'The campaign is not tracked';
let actualWR = await this.activity();
setInterval(async ()=>{
let newWR = await this.activity();
if (actualWR[0].id != newWR[0].id) {
let map = await newWR[0].map();
/**
* Emitted when a new WR is set.
* <info>This event is emitted only if the method {@link Campaign#subWR} is called</info>
* @event Campaign#wr
* @param {TMMap} map The map where the new WR is set.
* @param {CampaignRecordActivity} newWR The new WR.
*/
this.emit('wr', map, newWR);
actualWR = newWR;
}
}, this.client.options.cache.ttl * 60 * 1000);
}
}
/**
* The media images of an official campaign.
*/
class CampaignMedia {
constructor(client, data) {
const ReqUtil = require('../util/ReqUtil'),
tmioURL = new ReqUtil(client).tmioURL;
/**
* The client object of the campaign.
* @type {Client}
*/
this.client = client;
/**
* The decal image URL of the campaign.
* @type {string}
*/
this.decal = tmioURL + data.decal;
/**
* The button background image URL of the campaign.
* @type {string}
*/
this.buttonBackground = tmioURL + data.buttonbackground;
/**
* The button foreground image URL of the campaign.
* @type {string}
*/
this.buttonForeground = tmioURL + data.buttonforeground;
/**
* The live button background image URL of the campaign.
* @type {string}
*/
this.liveButtonBackground = tmioURL + data.livebuttonbackground;
/**
* The live button foreground image URL of the campaign.
* @type {string}
*/
this.liveButtonForeground = tmioURL + data.livebuttonforeground;
/**
* The popup background image URL of the campaign.
* @type {string}
*/
this.popupBackground = data.popupbackground;
/**
* The popup foreground image URL of the campaign.
* @type {string}
*/
this.popup = data.popup;
}
}
/**
* The leaderboard of a campaign
*/
class CampaignLeaderboard{
constructor(campaign, data){
/**
* The campaign
* @type {Campaign}
*/
this.campaign = campaign;
/**
* The client instance.
* @type {Client}
*/
this.client = this.campaign.client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* Fetches the player
* @returns {Promise<Player>}
*/
async player(){
let player = await this.client.players.get(this._data.player.id);
return player;
}
/**
* The player name
* @type {string}
*/
get playerName(){
return this._data.player.name;
}
/**
* The position
* @type {number}
*/
get position(){
return this._data.position;
}
/**
* The number of points
* @type {number}
*/
get points(){
return this._data.points;
}
}
/**
* The WR activity of a campaign
*/
class CampaignRecordActivity{
constructor(campaign, data){
/**
* The Campaign
* @type {Campaign}
*/
this.campaign = campaign;
/**
* The client instance.
* @type {Client}
*/
this.client = this.campaign.client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The ID of the activity
* @type {number}
*/
get id(){
return this._data.id;
}
/**
* The leaderboard UID of the campaign
* @type {string}
*/
get leaderboardId(){
return this._data.group;
}
/**
* The map of the activity
* @returns {Promise<TMMap>}
*/
async map(){
return await this.client.maps.get(this._data.map.mapUid);
}
/**
* The map name
* @type {string}
*/
get mapName(){
return this._data.map.name;
}
/**
* The map author
* @returns {Promise<Player>}
*/
async mapAuthor(){
return await this.client.players.get(this._data.map.author);
}
/**
* The map author name
* @type {string}
*/
get mapAuthorName(){
return this._data.map.authorplayer.name;
}
/**
* The player who set the record
* @returns {Promise<Player>}
*/
async player(){
return await this.client.players.get(this._data.player.id);
}
/**
* The player name who set the record
* @type {string}
*/
get playerName(){
return this._data.player.name;
}
/**
* The date of the record
* @type {Date}
*/
get date(){
return new Date(this._data.drivenat);
}
/**
* The time of the record
* @type {number}
*/
get time(){
return this._data.time;
}
/**
* The difference between the record and the previous one
* @type {number}
*/
get difference(){
return this._data.timediff;
}
}
module.exports = Campaign;

385
packages/bridge-shared/trackmania.io-f/src/structures/Club.js

@ -0,0 +1,385 @@
const Player = require('./Player'); // eslint-disable-line no-unused-vars
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const Campaign = require('./Campaign'); // eslint-disable-line no-unused-vars
const {ClubMemberRole} = require('../util/Constants'); // eslint-disable-line no-unused-vars
const CacheManager = require('../managers/CacheManager');
/**
* The Club class represents a club in Trackmania.
*/
class Club {
constructor(client, data) {
/**
* The client object of the club
* @type {Client}
*/
this.client = client;
/**
* The data of the club
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The Club ID
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The Club name
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* The Club tag
* @type {string}
*/
get tag() {
return this._data.tag;
}
/**
* The Club description
* @type {string}
*/
get description() {
return this._data.description;
}
/**
* The Club logo URL
* @type {string}
*/
get logo() {
return this._data.logoUrl;
}
/**
* The Club decal URL
* @type {string}
*/
get decal() {
return this._data.decalUrl;
}
/**
* The Club background URL
* @type {string}
*/
get background() {
return this._data.backgroundUrl;
}
/**
* The club vertical background URL
* @type {string}
*/
get vertical() {
return this._data.verticalUrl;
}
/**
* The club screens URL. Imares are in DDS format, except the sponsor 4x1 that may be in PNG.
* @type {Object<string, string>}
*/
get screens() {
return {
'2x1': this._data.screen2x1Url,
'16x9': this._data.screen16x9Url,
'Sponsor4x1': this._data.decalSponsor4x1Url,
'8x1': this._data.decal8x1Url,
'16x1': this._data.decal16x1Url,
};
}
/**
* The club creation date
* @type {Date}
*/
get createdAt() {
return new Date(this._data.creationTimestamp*1000);
}
/**
* The club popularity level
* @type {number}
*/
get popularity() {
return this._data.popularityLevel;
}
/**
* The club state (public/private)
* @type {string}
*/
get state() {
return this._data.state;
}
/**
* Whether the club is featured
* @type {boolean}
*/
get featured() {
return this._data.featured;
}
/**
* The club member count
* @type {number}
*/
get memberCount() {
return this._data.membercount;
}
/**
* The club creator player
* @returns {Promise<Player>}
* @example
* Client.clubs.get(54).then(async club => {
* const creator = await club.creator;
* console.log(creator.name);
* });
*/
async creator() {
return this.client.players.get(this._data.creatorplayer.id);
}
/**
* The club members (Members are sorted by role and club interaction time.)
* @param {number} [page=0] The page number
* @param {boolean} [cache=true] Whether to cache the result
* @returns {Promise<Array<ClubMember>>}
*/
async fetchMembers(page = 0, cache = true) {
if (!this._membersCache) {
/**
* The cache manager for members
* @type {CacheManager}
* @private
*/
this._membersCache = new CacheManager(this.client, this, ClubMember);
}
const club = this.client.options.api.paths.tmio.tabs.club,
members = this.client.options.api.paths.tmio.tabs.members,
ReqUtil = require('../util/ReqUtil');
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${club}/${this.id}/${members}/${page}`);
const array = [];
for (const member of res.members) {
array.push(new ClubMember(this, member));
if (cache) {
this._membersCache.set(member.player.id, member);
}
}
return array;
}
/**
* The club activities
* @param {number} [page=0] The page number
* @param {boolean} [cache=true] Whether to cache the result
* @returns {Promise<Array<ClubActivity>>}
*/
async fetchActivities(page = 0, cache = true) {
if (!this._activitiesCache) {
/**
* The cache manager for members
* @type {CacheManager}
* @private
*/
this._activitiesCache = new CacheManager(this.client, this, ClubActivity);
}
const club = this.client.options.api.paths.tmio.tabs.club,
activities = this.client.options.api.paths.tmio.tabs.activities,
ReqUtil = require('../util/ReqUtil');
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${club}/${this.id}/${activities}/${page}`);
const array = [];
for (const activity of res.activities) {
array.push(new ClubActivity(this, activity));
if (cache) {
this._activitiesCache.set(activity.id, activity);
}
}
return array;
}
}
/**
* Represents a club member (player) in the club
*/
class ClubMember {
constructor(club, data) {
/**
* The club object
* @type {Club}
*/
this.club = club;
/**
* The data of the club
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The member
* @returns {Promise<Player>}
*/
async member() {
return this.club.client.players.get(this._data.player.id);
}
/**
* The join date on the club
* @type {Date}
*/
get joinDate() {
return new Date(this._data.joinTime*1000);
}
/**
* The member role
* @type {ClubMemberRole}
*/
get role() {
return this._data.role;
}
/**
* Whether the member is a club creator
* @type {boolean}
*/
get isCreator() {
return this.role == 'Creator';
}
/**
* Whether the member is a club admin
* @type {boolean}
*/
get isAdmin() {
return this.role == 'Admin';
}
/**
* Whether the member is a vip
* @type {boolean}
*/
get isVip() {
return this._data.vip;
}
}
/**
* Represents a club activity in the club
*/
class ClubActivity {
constructor(club, data) {
/**
* The club object
* @type {Club}
*/
this.club = club;
/**
* The data of the club
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The activity id
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The activity name
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* The activity type
* @type {string}
*/
get type() {
return this._data.type;
}
/**
* Whether the activity is a public activity
* @type {boolean}
*/
get isPublic() {
return this._data.public;
}
/**
* The activity image URL
* @type {string}
*/
get media() {
return this._data.media;
}
/**
* Whether the activity is password protected
* @type {boolean}
*/
get isPasswordProtected() {
return this._data.password;
}
/**
* The activity external id
* @type {number}
*/
get externalId() {
return this._data.externalid;
}
/**
* If the activity is a campaign, returns the campaign object of the activity
* @returns {?Promise<Campaign>}
*/
async campaign() {
if (this.type === 'campaign') return this.club.client.campaigns.get(this.club.id, this.externalId);
else return null;
}
/**
* If the activity is a room, returns the room object of the activity
* @returns {?Promise<Room>}
*/
async room() {
if (this.type === 'room') return this.club.client.rooms.get(this.club.id, this.externalId);
else return null;
}
}
module.exports = Club;

353
packages/bridge-shared/trackmania.io-f/src/structures/Match.js

@ -0,0 +1,353 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const {MMTypes, MatchmakingGroup, MatchStatus, TeamNames, TeamName} = require('../util/Constants'); // eslint-disable-line no-unused-vars
const RoyalTeams = require('../data/RoyalTeams.json');
/**
* The match
*/
class Match {
constructor(client, data){
/**
* The client instance.
* @type {Client}
*/
this.client = client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The match ID
* @type {number}
*/
get id(){
return this._data.id;
}
/**
* The match live ID
* @type {string}
*/
get liveId(){
return this._data.lid;
}
/**
* The match name
* @type {string}
*/
get name(){
return this._data.name;
}
/**
* The match type
* @type {?MatchmakingGroup}
*/
get type(){
if (this.name.includes("3v3")) return MMTypes[2];
else if (this.name.includes("royal")) return MMTypes[3];
else return null;
}
/**
* The match group
* @type {string}
* @private
*/
get group(){
return this._data.group;
}
/**
* The match start date
* @type {Date}
*/
get startDate(){
return new Date(this._data.startdate * 1000);
}
/**
* The match end date
* @type {Date}
*/
get endDate(){
return new Date(this._data.enddate * 1000);
}
/**
* The match score direction
* @type {string}
* @private
*/
get scoreDirection(){
return this._data.scoredirection;
}
/**
* The match participant type
* @type {string}
* @private
*/
get participantType(){
return this._data.participanttype;
}
/**
* The match script settings
* NOTE: Array is empty (api update?)
* @type {Array<MatchScriptSetting>}
* @private
*/
get scriptSettings(){
return this._data.scriptsettings;
}
/**
* The match maps
* NOTE: Array is empty (api update?)
* @type {Array<MatchMap>}
* @private
*/
get maps(){
return this._data.maps;
}
/**
* The match server id
* @type {number}
*/
get serverId(){
return this._data.serverid;
}
/**
* The match join link
* @type {string}
*/
get joinLink(){
return this._data.serverjoinlink;
}
/**
* The match status
* @type {MatchStatus}
*/
get status(){
return this._data.status;
}
/**
* The match players
* @type {Array<MatchPlayer>}
*/
get players(){
return this._data.players.map(player => new MatchPlayer(this, player));
}
/**
* The match teams (if match is completed)
* @type {?Array<MatchTeam>}
*/
get teams(){
if(this.status === "COMPLETED"){
if (this.type == MMTypes[3]) {
// In royal, there are 20 teams but the API returns 30 items, so we need to delete the last 10 teams
return this._data.teams.slice(0, 20).map(team => new MatchTeam(this, team));
}
return this._data.teams.map(team => new MatchTeam(this, team));
}
return null;
}
}
/**
* The team in the match
*/
class MatchTeam {
constructor(match, data){
/**
* The match
* @type {Match}
*/
this.match = match;
/**
* The client instance.
* @type {Client}
*/
this.client = match.client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The team Index
* @type {number}
*/
get index(){
return this._data.index;
}
/**
* The team score
* @type {number}
*/
get score(){
return this._data.score;
}
/**
* The team name
* @type {?string | TeamName}
*/
get name(){
if (this.match.type == MMTypes[2]){ // 3v3
return TeamNames[this.index];
} else if (this.match.type == MMTypes[3]){ // Royal
return RoyalTeams[this.index].name;
}
return null;
}
/**
* The team image (if the match is Royal)
* @type {?string}
*/
get image(){
if (this.match.type == MMTypes[3]){ // Royal
return RoyalTeams[this.index].img;
}
return null;
}
}
/**
* The player in the match
*/
class MatchPlayer {
constructor(match, data){
/**
* The match
* @type {Match}
*/
this.match = match;
/**
* The client instance.
* @type {Client}
*/
this.client = match.client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The player name
* @type {string}
*/
get name(){
return this._data.player.name;
}
/**
* The player id
* @type {string}
*/
get id(){
return this._data.player.id;
}
/**
* The player rank in the match
* @type {number}
*/
get rank(){
return this._data.rank;
}
/**
* The player score in the match
* @type {number}
*/
get score(){
return this._data.score;
}
/**
* The team index where the player is in the match
* @type {number}
*/
get teamIndex(){
return this._data.team;
}
/**
* The team where the player is (if the match is completed)
* @type {?MatchTeam}
*/
get team(){
if(this.match.status === "COMPLETED"){
return this.match.teams[this.teamIndex];
}
return null;
}
/**
* Whether the player is MVP (in a 3v3 match)
* @type {boolean}
*/
get isMVP(){
if (this._data.mvp) return this._data.mvp;
else return false;
}
/**
* The matchmaking points of the player before the match
* @type {number}
*/
get mmPointsBefore(){
return this._data.matchmakingpoints.before;
}
/**
* The matchmaking points of the player after the match
* @type {number}
*/
get mmPointsAfter(){
return this._data.matchmakingpoints.after;
}
/**
* The matchmaking points of the player gained in the match
* @type {number}
*/
get mmPointsGained(){
return this.mmPointsAfter - this.mmPointsBefore;
}
/**
* The player object
* @type {Player}
*/
async player(){
return this.client.players.get(this.id);
}
}
module.exports = Match;

104
packages/bridge-shared/trackmania.io-f/src/structures/MatchmakingDivision.js

@ -0,0 +1,104 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const MatchmakingDivisionData = require('../data/MatchmakingDivisions.json');
const { MMTypes } = require('../util/Constants');
/**
* Represents a division in the matchmaking system.
*/
class MatchmakingDivision{
constructor(client, typeId, division){
/**
* The client instance
* @type {Client}
*/
this.client = client;
/**
* The type of the division
* @type {number}
*/
this.typeId = typeId;
/**
* The division data
* @type {Object}
* @private
*/
this._division = division;
}
/**
* The division position
* @type {number}
*/
get position(){
return this._division.position;
}
/**
* The type name of the division
* @type {string}
*/
get typeName(){
return MMTypes[this.typeId];
}
/**
* The name of the division
* @type {string}
*/
get name(){
if (this.typeName == null) return null;
return MatchmakingDivisionData[this.typeName][this.position].name;
}
/**
* The rule identifier of this division
* @type {string}
*/
get rule(){
return this._division.rule;
}
/**
* The minimum points to the division
* @type {number}
*/
get minPoints(){
if (this.typeId == 3) return this._division.minwins;
else return this._division.minpoints;
}
/**
* The maximum points to the division
* @type {number}
*/
get maxPoints(){
if (this.typeId == 3) return this._division.maxwins;
else return this._division.maxpoints;
}
/**
* The image of the division. If Royal, the crown
* @type {string}
*/
get image(){
if (this.typeName == null) return null;
if (this.typeName === "3v3") return MatchmakingDivisionData[this.typeName][this.position].img;
else if (this.typeName === "Royal") return MatchmakingDivisionData[this.typeName][this.position].img.crown;
else return null;
}
/**
* The Royal Lion image of the division
* @type {string}
*/
get lion(){
if (this.typeName == null) return null;
else if (this.typeName === "Royal") return MatchmakingDivisionData[this.typeName][this.position].img.lion;
else return null;
}
}
module.exports = MatchmakingDivision;

1303
packages/bridge-shared/trackmania.io-f/src/structures/Player.js

File diff suppressed because it is too large

163
packages/bridge-shared/trackmania.io-f/src/structures/Room.js

@ -0,0 +1,163 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const TMMap = require('./TMMap'); // eslint-disable-line no-unused-vars
const Club = require('./Club'); // eslint-disable-line no-unused-vars
const {RoomRegion} = require('../util/Constants'); // eslint-disable-line no-unused-vars
/**
* Represents a room.
*/
class Room {
constructor(client, data) {
/**
* The client that instantiated this room
* @type {Client}
*/
this.client = client;
/**
* The data of the room
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The ID of the room
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The club that this room belongs to
* @returns {Promise<Club>}
*/
async club() {
return this.client.clubs.get(this._data.clubid);
}
/**
* The name of the room
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* Whether this room is hosted on the cloud (Nadeo)
* @type {boolean}
*/
get isCloud() {
return this._data.nadeo;
}
/**
* The login of the room (if it's not a cloud room)
* @type {?string}
*/
get login() {
if (!this.isCloud) return this._data.login;
else return null;
}
/**
* The number of players in the room
* @type {number}
*/
get playerCount() {
return this._data.playercount;
}
/**
* The maximum number of players in the room
* @type {number}
*/
get maxPlayersCount() {
return this._data.playermax;
}
/**
* The region of the room (if it's on a cloud room)
* @type {RoomRegion}
*/
get region() {
if (this.isCloud) return this._data.region;
else return null;
}
/**
* The script name that currently runs in the room
* @type {string}
*/
get script() {
return this._data.script;
}
/**
* The script settings on the room.
* <info> Example of result:
*
* [{
*
* key: 'S_DecoImageUrl_Checkpoint',
*
* value: 'https://trackmania-prod-nls-file-store-s3.cdn.ubi.com/club/decal/5f62400600952.png?updateTimestamp=1600274438.png'
*
* }]
*
* </info>
* @type {Array<Object<string, string|number|boolean>>} x must be "key" and "value"
*/
get scriptSettings() {
var array = [];
Object.entries(this._data.scriptSettings).forEach(([res]) => {
let [key, value] = res;
if (value.type == 'text') {
value = String(value.value);
} else if (value.type == 'integer') {
value = Number(value.value);
} else if (value.type == 'boolean') {
value = Boolean(value.value);
}
array.push({
key,
value
});
});
return array;
}
/**
* The image URL of the room
* @type {string}
*/
get imageUrl() {
return this._data.mediaurl;
}
/**
* The maps on the room
* @returns {Promise<Array<TMMap>>}
* @example
* Client.rooms.get(228, 82160).then(async room => {
* const maps = await room.maps();
* maps.forEach(map => console.log(map.name));
* });
*/
async maps() {
const array = [];
for (let i = 0; i < this._data.maps.length; i++) {
let map = await this.client.maps.get(this._data.maps[i].mapUid);
array.push(map);
}
return array;
}
}
module.exports = Room;

71
packages/bridge-shared/trackmania.io-f/src/structures/Splashscreen.js

@ -0,0 +1,71 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
/**
* Represents a news item.
*/
class Splashscreen {
constructor(client, data) {
/**
* The client object of the news page
* @type {Client}
*/
this.client = client;
/**
* The data of the news page
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The news item's id
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The news item's headline
* @type {string}
*/
get headline() {
return this._data.headline;
}
/**
* The news item's text content
* @type {string}
*/
get body() {
return this._data.body;
}
/**
* The link to the news item on trackmania.com
* @type {string}
*/
get link() {
return this._data.link;
}
/**
* The news item's accompanying media file as a link
* @type {string}
*/
get media() {
return this._data.media;
}
/**
* The news item's time of publishing
* @type {Date}
*/
get time() {
return new Date(this._data.time * 1000);
}
}
module.exports = Splashscreen;

636
packages/bridge-shared/trackmania.io-f/src/structures/TMEvent.js

@ -0,0 +1,636 @@
const Player = require('./Player'); // eslint-disable-line no-unused-vars
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const TMMap = require('./TMMap'); // eslint-disable-line no-unused-vars
const ReqUtil = require('../util/ReqUtil');
const CacheManager = require('../managers/CacheManager');
/**
* Represents a Event in Trackmania.
*/
class TMEvent {
constructor(client, data) {
/** The client instance
* @type {Client}
*/
this.client = client;
/**
* The event's data.
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The event's ID.
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The number of players in the event.
* @type {number}
*/
get size() {
return this._data.numplayers;
}
/**
* The event's Live ID.
* @type {string}
*/
get liveId() {
return this._data.liveid;
}
/**
* The creator of the event.
* @returns {Promise<Player>}
*/
async creator() {
return this.client.players.get(this._data.creatorplayer.id);
}
/**
* The event's name.
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* The event's description.
* @type {string}
*/
get description() {
return this._data.description;
}
/**
* The event's registration start date.
* @type {Date}
*/
get registrationStart() {
return new Date(this._data.registrationstart * 1000);
}
/**
* The event's registration end date.
* @type {Date}
*/
get registrationEnd() {
return new Date(this._data.registrationend * 1000);
}
/**
* The event's start date.
* @type {Date}
*/
get start() {
return new Date(this._data.startdate * 1000);
}
/**
* The event's end date.
* @type {Date}
*/
get end() {
return new Date(this._data.enddate * 1000);
}
/**
* The event's leaderboard id.
* @type {number}
*/
get leaderboardId() {
return this._data.leaderboardid;
}
/**
* The event's manialink (if any).
* @type {?string}
*/
get manialink() {
if (this._data.manialink == "") return null;
return this._data.manialink;
}
/**
* The event's rules URL (if any).
* @type {?string}
*/
get rulesUrl() {
if (this._data.rulesurl == "") return null;
return this._data.rulesurl;
}
/**
* The event's stream URL (if any).
* @type {?string}
*/
get stream() {
if (this._data.streamurl == "") return null;
return this._data.streamurl;
}
/**
* The event's website (if any).
* @type {?string}
*/
get website() {
if (this._data.websiteurl == "") return null;
return this._data.websiteurl;
}
/**
* The event's logo URL.
* @type {string}
*/
get logo() {
return new ReqUtil(this.client).tmioURL + this._data.logourl;
}
/**
* The event's vertical banner URL.
* @type {string}
*/
get vertical() {
return new ReqUtil(this.client).tmioURL + this._data.verticalurl;
}
/**
* The event's rounds.
* @type {Array<TMEventRound>}
*/
get rounds() {
const arr = [];
for (let i = 0; i < this._data.rounds.length; i++) {
arr.push(new TMEventRound(this, this._data.rounds[i]));
}
return arr;
}
}
/**
* Represents a round in a TMEvent.
*/
class TMEventRound {
constructor(event, data) {
/**
* The event instance
* @type {TMEvent}
*/
this.event = event;
/**
* The client instance
* @type {Client}
*/
this.client = event.client;
/**
* The round's data.
* @type {Object}
* @private
*/
this._data = data;
/**
* The challenge's CacheManager instance
* @type {CacheManager}
* @private
*/
this._challengesCache = new CacheManager(this.client, this, TMEventChallenge);
}
/**
* The round's ID.
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The round's name.
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* The round's status.
* @type {string}
*/
get status() {
return this._data.status;
}
/**
* The round's matches.
* @type {Array<TMEventRoundMatch>}
*/
get matches() {
const arr = [];
for (let i = 0; i < this._data.matches.length; i++) {
arr.push(new TMEventRoundMatch(this, this._data.matches[i]));
}
return arr;
}
/**
* The round's challenges.
* @param {boolean} [cache=this.client.options.cache.enabled] Wether to get the challenges from the cache or not.
* @returns {Promise<Array<TMEventChallenge>>}
*/
async challenges(cache = this.client.options.cache.enabled) {
const arr = [];
for (let i = 0; i < this._data.challenges.length; i++) {
if (cache && this._challengesCache.has(this._data.challenges[i].id)) {
arr.push(this._challengesCache.get(this._data.challenges[i].id));
} else {
arr.push(await this._fetchChallenge(i, cache));
}
}
return arr;
}
/**
* Fetches the round's challenges.
* @param {number} index The index of the challenge to fetch.
* @param {boolean} [cache=this.client.options.cache.enabled] Wether to cache the challenges or not.
* @returns {Promise<Array<TMEventChallenge>>}
* @private
*/
async _fetchChallenge(index, cache = this.client.options.cache.enabled) {
const comp = this.client.options.api.paths.tmio.tabs.comp,
challenge = this.client.options.api.paths.tmio.tabs.challenge;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${comp}/${this.event.id}/${challenge}/${this._data.challenges[index].id}`);
const theChallenge = new TMEventChallenge(this, res);
if (cache) {
res._cachedTimestamp = Date.now();
this._challengesCache.set(this._data.challenges[index].id, theChallenge);
}
return theChallenge;
}
}
/**
* Represents a match in a TMEventRound.
*/
class TMEventRoundMatch {
constructor(round, data) {
/**
* The round instance
* @type {TMEventRound}
*/
this.round = round;
/**
* The event instance
* @type {TMEvent}
*/
this.event = round.event;
/**
* The client instance
* @type {Client}
*/
this.client = round.client;
/**
* The match's data.
* @type {Object}
* @private
*/
this._data = data;
/**
* The match's results CacheManager instance
* @type {CacheManager}
* @private
*/
this._resultsCache = new CacheManager(this.client, this, TMEventRoundMatchResult);
}
/**
* The match's ID.
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The match's name.
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* Whether the match is completed.
* @type {boolean}
*/
get isCompleted() {
return this._data.completed;
}
/**
* The match's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the results.
* @returns {Promise<Array<TMEventRoundMatchResult>>}
*/
async getResults(page = 0, cache = this.client.options.cache.enabled) {
if (cache && this._resultsCache.has(this.id+"_"+page)) {
return this._resultsCache.get(this.id+"_"+page);
} else {
return await this._fetchResults(page, cache);
}
}
/**
* Fetches the match's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the results.
* @returns {Promise<Array<TMEventRoundMatchResult>>}
* @private
*/
async _fetchResults(page = 0, cache = this.client.options.cache.enabled) {
const comp = this.client.options.api.paths.tmio.tabs.comp,
match = this.client.options.api.paths.tmio.tabs.match;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${comp}/${this.event.id}/${match}/${this.id}/${page}`);
const arr = [];
for (let i = 0; i < res.results.length; i++) {
const results = new TMEventRoundMatchResult(this, res.results[i]);
arr.push(results);
}
if (cache) {
res._cachedTimestamp = Date.now();
this._resultsCache.set(this.id+"_"+page, arr);
}
return arr;
}
}
/**
* Represents a result in a TMEventRoundMatch.
*/
class TMEventRoundMatchResult {
constructor(match, data) {
/**
* The match instance
* @type {TMEventRoundMatch}
*/
this.match = match;
/**
* The event instance
* @type {TMEvent}
*/
this.event = match.event;
/**
* The client instance
* @type {Client}
*/
this.client = match.client;
/**
* The result's data.
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The player that got the result.
* @returns {Promise<Player>}
*/
async player() {
return this.client.players.get(this._data.player.id);
}
/**
* The position of the player.
* @type {number}
*/
get position() {
return this._data.position;
}
/**
* The score of the player.
* @type {number}
*/
get score() {
return this._data.score;
}
}
/**
* Represents a challenge in a TMEventRound.
*/
class TMEventChallenge {
constructor(round, data) {
/**
* The round instance
* @type {TMEventRound}
*/
this.round = round;
/**
* The event instance
* @type {TMEvent}
*/
this.event = round.event;
/**
* The client instance
* @type {Client}
*/
this.client = round.client;
/**
* The challenge's data.
* @type {Object}
* @private
*/
this._data = data;
/**
* The challenge's results CacheManager instance
* @type {CacheManager}
* @private
*/
this._resultsCache = new CacheManager(this.client, this, TMEventChallengeResult);
}
/**
* The challenge's ID.
* @type {number}
*/
get id() {
return this._data.id;
}
/**
* The challenge's name.
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* The challenge's status.
* @type {string}
*/
get status() {
return this._data.status;
}
/**
* The challenge's rooms number.
* @type {number}
*/
get rooms() {
return this._data.servers;
}
/**
* The challenge's maps.
* @returns {Promise<Array<TMMap>>}
*/
async getMaps() {
const maps = [];
for (let i = 0; i < this._data.maps.length; i++) {
const map = await this.client.maps.get(this._data.maps[i].mapUid);
maps.push(map);
}
return maps;
}
/**
* The challenge's admins.
* @returns {Promise<Array<Player>>}
*/
async getAdmins() {
const admins = [];
for (let i = 0; i < this._data.admins.length; i++) {
const admin = await this.client.players.get(this._data.admins[i].accountid);
admins.push(admin);
}
return admins;
}
/**
* The challenge's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the results from cache.
* @returns {Promise<Array<TMEventChallengeResult>>}
*/
async getResults(page = 0 , cache = this.client.options.cache.enabled) {
if (cache && this._resultsCache.has(this.id+"_"+page)) {
return this._resultsCache.get(this.id+"_"+page);
} else {
return await this._fetchResults(page, cache);
}
}
/**
* Fetches the match's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the results.
* @returns {Promise<Array<TMEventChallengeResult>>}
* @private
*/
async _fetchResults(page = 0, cache = this.client.options.cache.enabled) {
const comp = this.client.options.api.paths.tmio.tabs.comp,
challenge = this.client.options.api.paths.tmio.tabs.challenge;
const res = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${comp}/${this.event.id}/${challenge}/${this.id}/${page}`);
const arr = [];
for (let i = 0; i < res.results.length; i++) {
const results = new TMEventChallengeResult(this, res.results[i]);
arr.push(results);
}
if (cache) {
res._cachedTimestamp = Date.now();
this._resultsCache.set(this.id+"_"+page, arr);
}
return arr;
}
}
/**
* Represents a result in a TMEventChallenge.
*/
class TMEventChallengeResult {
constructor(challenge, data) {
/**
* The challenge instance
* @type {TMEventChallenge}
*/
this.challenge = challenge;
/**
* The event instance
* @type {TMEvent}
*/
this.event = challenge.event;
/**
* The client instance
* @type {Client}
*/
this.client = challenge.client;
/**
* The result's data.
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The player.
* @returns {Promise<Player>}
*/
async player() {
return this.client.players.get(this._data.player.id);
}
/**
* The position of the player.
* @type {number}
*/
get position() {
return this._data.position;
}
/**
* The score of the player.
* @type {number}
*/
get score() {
return this._data.score;
}
}
module.exports = TMEvent;

480
packages/bridge-shared/trackmania.io-f/src/structures/TMMap.js

@ -0,0 +1,480 @@
const ReqUtil = require('../util/ReqUtil');
const Player = require('./Player'); // eslint-disable-line no-unused-vars
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const EventEmitter = require('events');
/**
* Represents a map on Trackmania.
*/
class TMMap extends EventEmitter {
constructor(client, data) {
super();
/**
* The client instance.
* @type {Client}
*/
this.client = client;
/**
* The map data.
* @type {Object}
* @private
*/
this._data = data;
/**
* The map medal times.
* @type {TMMapMedalTimes}
*/
this.medalTimes = new TMMapMedalTimes(this);
/**
* The map cached leaderboard data. You should use the leaderboardLoadMore() the first time to load the leaderboard.
* @type {Array<TMMapLeaderboard>}
*/
this.leaderboard = [];
}
/**
* The map name.
* @type {string}
*/
get name() {
return this._data.name;
}
/**
* The map id.
* @type {string}
*/
get id() {
return this._data.mapId;
}
/**
* The map unique id.
* @type {string}
*/
get uid() {
return this._data.mapUid;
}
/**
* The map Storage Object ID.
* @type {string}
*/
get storageId() {
return this.thumbnail.replace(/^[a-z:/.]*\/([^]*)\.[a-z]*$$/gi, '$1');
}
/**
* The map author's name.
* @type {string}
*/
get authorName() {
return this._data.authorplayer.name;
}
/**
* The map author.
* @returns {Promise<Player>}
* @example
* Client.maps.get('z28QXoFnpODEGgg8MOederEVl3j').then(async map => {
* const author = await map.author();
* console.log(`The map author is ${author.name}`);
* });
*/
async author() {
return this.client.players.get(this._data.author);
}
/**
* The map submitter's name.
* @type {string}
*/
get submitterName() {
return this._data.submitterplayer.name;
}
/**
* The map submitter.
* @returns {Promise<Player>}
*/
async submitter() {
return this.client.players.get(this._data.submitter);
}
/**
* The environment for this map.
* @type {string}
*/
get environment() {
return this._data.environment;
}
/**
* The map file name.
* @type {string}
*/
get fileName() {
return this._data.filename;
}
/**
* The map uploaded date.
* @type {Date}
*/
get uploaded() {
return new Date(this._data.timestamp);
}
/**
* The map URL.
* @type {string}
*/
get url() {
return this._data.fileUrl;
}
/**
* The map thumbnail (from Nadeo services, direct download).
* @type {string}
*/
get thumbnail() {
return this._data.thumbnailUrl;
}
/**
* The map thumbnail (cached from trackmania.io, can show).
* @type {string}
*/
get thumbnailCached() {
return `${new ReqUtil(this.client).tmioAPIURL}/download/jpg/${this.storageId}`;
}
/**
* The map exchange id, if the map is on trackmania.exchange, else null.
* @type {?string}
*/
get exchangeId() {
if (this._data.exchangeId == 0) return null;
else return this._data.exchangeid;
}
/**
* The map informations on trackmania.exchange.
* @returns {Promise<?TMExchangeMap>}
*/
async exchange() {
return new Promise((resolve, reject) => {
if (!this.exchangeId) return resolve(null);
const tmxurl = this.client.options.api.paths.tmx;
if (!this._data.exchange) {
this.client._apiReq(`${tmxurl.protocol}://${tmxurl.host}/${tmxurl.api}/${tmxurl.tabs.mapInfo}/${this.exchangeId}`).then(data => {
this._data.exchange = data[0];
return resolve(new TMExchangeMap(this.client, data[0]));
}).catch(err => {
return reject(err);
});
} else {
return resolve(new TMExchangeMap(this.client, this._data.exchange));
}
});
}
/**
* Load more results in the leaderboard.
* @param {number} [nbOfResults=100] The number of results to load. (max 100)
* @returns {Promise<?Array<TMMapLeaderboard>>}
*/
async leaderboardLoadMore(nbOfResults = 100) {
if (nbOfResults > 100) nbOfResults = 100;
if (nbOfResults < 1) nbOfResults = 1;
const leaderboard = this.client.options.api.paths.tmio.tabs.leaderboard,
map = this.client.options.api.paths.tmio.tabs.map,
params = new URLSearchParams();
params.append('offset', this.leaderboard.length);
params.append('length', nbOfResults);
const leaderboardRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${leaderboard}/${map}/${this.uid}?${params.toString()}`);
if (leaderboardRes.tops != null){
for (let i = 0; i < leaderboardRes.tops.length; i++) {
this.leaderboard.push(new TMMapLeaderboard(this, leaderboardRes.tops[i]));
}
}
return this.leaderboard;
}
/**
* Get a leaderboard in a specific position. Must be between 1 and 10000.
* @param {number} position The position of the leaderboard.
* @returns {Promise<?TMMapLeaderboard>}
*/
async leaderboardGet(position){
if (position < 1 || position > 10000) throw "Position must be between 1 and 10000";
position--;
const leaderboard = this.client.options.api.paths.tmio.tabs.leaderboard,
map = this.client.options.api.paths.tmio.tabs.map,
leaderboardRes = await this.client._apiReq(`${new ReqUtil(this.client).tmioAPIURL}/${leaderboard}/${map}/${this.uid}?offset=${position}&length=1`);
if (!leaderboardRes.tops) return null;
return new TMMapLeaderboard(this, leaderboardRes.tops[0]);
}
/**
* Subscribe to the map WR updates.
* <info>When a new WR is set, the event {@link TMMap#e-wr} will be fired</info>
* @returns {Promise<void>}
* @example
* Client.maps.get('z28QXoFnpODEGgg8MOederEVl3j').then(map => {
* map.subWR();
* map.on('wr', (old, new) => {
* console.log(`New WR for ${map.name} is ${new.playerName} (${new.time})`);
* });
* });
*/
async subWR() {
let actualWR = await this.leaderboardGet(1);
setInterval(async ()=>{
let newWR = await this.leaderboardGet(1);
if (actualWR.time != newWR.time) {
/**
* Emitted when a new WR is set.
* <info>This event is emitted only if the method {@link TMMap#subWR} is called</info>
* @event TMMap#wr
* @param {TMMapLeaderboard} oldWR The old WR.
* @param {TMMapLeaderboard} newWR The new WR.
*/
this.emit('wr', actualWR, newWR);
actualWR = newWR;
}
}, this.client.options.cache.ttl * 60 * 1000);
}
}
/**
* Represents the medals times on a map.
*/
class TMMapMedalTimes {
constructor(map) {
/**
* The map object.
* @type {TMMap}
*/
this.map = map;
/**
* The map author time.
* @type {number}
*/
this.author = map._data.authorScore;
/**
* The map gold time.
* @type {number}
*/
this.gold = map._data.goldScore;
/**
* The map silver time.
* @type {number}
*/
this.silver = map._data.silverScore;
/**
* The map bronze time.
* @type {number}
*/
this.bronze = map._data.bronzeScore;
}
}
/**
* Represents the map details from Trackmania.exchange.
*/
class TMExchangeMap {
constructor(map, data) {
/**
* The map instance.
* @type {TMMap}
*/
this.map = map;
/**
* The client instance.
* @type {Client}
*/
this.client = map.client;
/**
* The map data.
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The map exchange id.
* @type {number}
*/
get id() {
return this._data.TrackID;
}
/**
* The map name.
* @type {string}
*/
get name() {
return this._data.Name;
}
/**
* The map author.
* @type {string}
*/
get author() {
return this._data.Username;
}
/**
* The map description.
* @type {string}
*/
get description() {
return this._data.Comments;
}
/**
* The map length.
* @type {string}
*/
get length() {
return this._data.LengthName;
}
/**
* The map difficulty.
* @type {string}
*/
get difficulty() {
return this._data.DifficultyName;
}
/**
* The map upload date.
* @type {Date}
*/
get uploaded() {
return new Date(this._data.UploadedAt);
}
/**
* The map last update date.
* @type {Date}
*/
get updated() {
return new Date(this._data.UpdatedAt);
}
/**
* The map award count.
* @type {number}
*/
get awards() {
return this._data.AwardCount;
}
/**
* The map download link.
* @type {string}
*/
get download() {
const tmx = this.map.client.options.api.paths.tmx;
return `${tmx.protocol}://${tmx.host}/${tmx.tabs.mapsDownload}/${this.id}`;
}
}
/**
* Represents the map leaderboard.
*/
class TMMapLeaderboard {
constructor(map, data) {
/**
* The map Instance
* @type {TMMap}
*/
this.map = map;
/**
* The Client instance
* @type {Client}
*/
this.client = map.client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The player that got this leaderboard
* @returns {Promise<Player>}
*/
async player(){
return this.client.players.get(this._data.player.id);
}
/**
* The player name on this leaderboard
* @type {string}
*/
get playerName(){
return this._data.player.name;
}
/**
* The player club tag on this leaderboard
* @type {string}
*/
get playerClubTag(){
return this._data.player.tag;
}
/**
* The position of the player on this leaderboard
* @type {number}
*/
get position(){
return this._data.position;
}
/**
* The time in milliseconds of the player
* @type {number}
*/
get time(){
return this._data.time;
}
/**
* The date when the player get this leaderboard
* @type {Date}
*/
get date() {
return new Date(this._data.timestamp);
}
/**
* The ghost URL
* @type {string}
*/
get ghost(){
return `${new ReqUtil(this.client).tmioURL}${this._data.url}`;
}
}
module.exports = TMMap;

85
packages/bridge-shared/trackmania.io-f/src/structures/TOTD.js

@ -0,0 +1,85 @@
const Client = require('../client/Client'); // eslint-disable-line no-unused-vars
const TMMap = require('../structures/TMMap'); // eslint-disable-line no-unused-vars
/**
* Represents a Track Of The Day (TOTD).
*/
class TOTD {
constructor(client, data){
/**
* The client objet
* @type {Client}
*/
this.client = client;
/**
* The data
* @type {Object}
* @private
*/
this._data = data;
}
/**
* The map
* @returns {Promise<TMMap>}
* @example
* Client.totd.get(date).then(async totd=>{
* const map = await totd.map();
* console.log(map.name);
* })
*/
async map(){
return this.client.maps.get(this._data.map.mapUid);
}
/**
* The campaign ID
* @type {number}
*/
get campaignId(){
return this._data.campaignid;
}
/**
* The week day
* @type {number}
*/
get weekDay(){
return this._data.weekday;
}
/**
* The month day
* @type {number}
*/
get monthDay(){
return this._data.monthday;
}
/**
* The month
* @type {number}
*/
get month(){
return this._data.month;
}
/**
* The year
* @type {number}
*/
get year(){
return this._data.year;
}
/**
* The leaderboard ID
* @type {string}
*/
get leaderboardId(){
return this._data.leaderboarduid;
}
}
module.exports = TOTD;

116
packages/bridge-shared/trackmania.io-f/src/util/Constants.js

@ -0,0 +1,116 @@
const { createEnum } = require("./Util");
/**
* All available player groups.
* * `nadeo` - All players from the Nadeo company
* * `tmgl` - All players from the TMGL competition
* * `sponsor` - All players who sponsors Trackmania.io and Openplanet
* * `team` - All players from the Trackmania.io and Openplanet team
* @typedef {string} PlayerGroup
*/
exports.GroupTypes = createEnum([
'nadeo',
'tmgl',
'sponsor',
'team'
]);
/**
* All available matchmaking groups.
* * `3v3`
* * `royal`
* @typedef {string} MatchmakingGroup
*/
exports.MMTypes = createEnum([
...Array(2).fill(null),
'3v3', // 2
'royal' // 3
]);
/**
* All available Nadeo-hosted club rooms regions.
* * `eu-west` - Europe West
* * `ca-central` - Canada Central
* @typedef {string} RoomRegion
*/
exports.RoomRegions = createEnum([
'eu-west',
'ca-central'
]);
/**
* All available Club Member roles.
* * `Creator`
* * `Admin`
* * `Content_Creator`
* * `Member`
* @typedef {string} ClubMemberRole
*/
exports.ClubMemberRoles = createEnum([
'Creator',
'Admin',
'Content_Creator',
'Member'
]);
/**
* All available Maniapub Types.
* * `nadeo` - Nadeo official Maniapubs
* * `ugc` - (User Generated Content) - Community Maniapubs
* @typedef {string} AdType
*/
exports.AdTypes = createEnum([
'nadeo',
'ugc'
]);
/**
* All available Match Status types.
* * `HAS_MATCHES`
* * `ONGOING`
* * `PENDING`
* * `COMPLETED`
* @typedef {string} MatchStatus
*/
exports.MatchStatus = createEnum([
'HAS_MATCHES',
'ONGOING',
'PENDING',
'COMPLETED'
]);
/**
* Team names for the 3v3 match.
* * `Red`
* * `Blue`
* @typedef {string} TeamName
*/
exports.TeamNames = createEnum([
'Blue',
'Red'
]);
/**
* All available COTD leaderboard sorting groups.
* * `wins`
* * `winstreak`
* * `totalplayed`
* @typedef {string} COTDLeaderboardSortGroup
*/
exports.COTDLeaderboardSortGroups = createEnum([
'wins',
'winstreak',
'totalplayed'
]);
/**
* @typedef {Object} Constants Constants that can be used in an enum or object-like way.
* @property {PlayerGroup} GroupTypes All available player groups.
* @property {MatchmakingGroup} MMTypes All available matchmaking groups.
* @property {RoomRegion} RoomRegions All available Nadeo-hosted club rooms regions.
* @property {ClubMemberRole} ClubMemberRoles All available Club Member roles.
* @property {AdType} AdTypes All available Maniapub Types.
* @property {MatchStatus} MatchStatus All available Match Status types.
* @property {TeamName} TeamNames All available Team names for the 3v3 match.
* @property {COTDLeaderboardSortGroup} COTDLeaderboardSortGroups All available COTD leaderboard sorting groups.
*/

31
packages/bridge-shared/trackmania.io-f/src/util/ReqUtil.js

@ -0,0 +1,31 @@
/**
* Util for API requests
* @private
*/
class ReqUtil {
constructor(client){
this.client = client;
}
get tmioAPIURL(){
const tmio = this.client.options.api.paths.tmio;
return `${tmio.protocol}://${tmio.host}/${tmio.api}`;
}
get tmioURL(){
const tmio = this.client.options.api.paths.tmio;
return `${tmio.protocol}://${tmio.host}`;
}
get tmxAPIURL(){
const tmx = this.client.options.api.paths.tmx;
return `${tmx.protocol}://${tmx.host}/${tmx.api}`;
}
get votingAPIURL(){
const mapVoting = this.client.options.api.paths.mapVoting;
return `${mapVoting.protocol}://${mapVoting.host}`;
}
}
module.exports = ReqUtil;

34
packages/bridge-shared/trackmania.io-f/src/util/Util.js

@ -0,0 +1,34 @@
/**
* Sets default properties on an object that aren't already specified.
* @param {Object} def Default properties
* @param {Object} given Object to assign defaults to
* @returns {Object}
* @private
*/
exports.mergeDefault = (def, given) => {
if (!given) return def;
for (const key in def) {
if (!Object.prototype.hasOwnProperty.call(given, key) || given[key] === undefined) {
given[key] = def[key];
} else if (given[key] === Object(given[key])) {
given[key] = this.mergeDefault(def[key], given[key]);
}
}
return given;
};
/**
* Create an enum from a list of strings.
* @param {Array<string>} keys
* @returns {Object}
* @private
*/
exports.createEnum = (keys) => {
const obj = {};
for (const [index, key] of keys.entries()) {
if (key === null) continue;
obj[key] = index;
obj[index] = key;
}
return obj;
};

421
packages/bridge-shared/trackmania.io-f/src/util/defaultOptions.js

@ -0,0 +1,421 @@
/**
* The default options.
*/
class defaultOptions {
constructor(baseClient) {
/**
* The base client instance.
* @type {BaseClient}
* @private
*/
this._baseClient = baseClient;
/**
* The default API options.
* @type {defaultOptionsAPI}
*/
this.api = new defaultOptionsAPI();
/**
* The default Cache options.
* @type {defaultOptionsCache}
*/
this.cache = new defaultOptionsCache();
/**
* Whether is in dev mode (Uses more logging traces in the core).
* @type {boolean}
*/
this.dev = false;
}
}
/**
* The default API options.
*/
class defaultOptionsAPI {
constructor(defaultOptions) {
/**
* The default options.
* @type {defaultOptions}
* @private
* @readonly
*/
this._defaultOptions = defaultOptions;
/**
* The default API Paths options.
* @type {defaultOptionsAPIPaths}
*/
this.paths = new defaultOptionsAPIPaths();
if (!this.useragent && 'TMIO_UA' in process.env) {
/**
* The default User Agent to use.
* If present, this defaults to `process.env.TMIO_UA` when instantiating the client
* @type {?string}
*/
this.useragent = process.env.TMIO_UA;
} else {
this.useragent = null;
}
if (!this.key && 'TMIO_API' in process.env) {
/**
* The API Key to use. It must contains "yourname:theactualsecretkey".
* If present, this defaults to `process.env.TMIO_API` when instantiating the client
*
* <warn>This should be kept private at all times.</warn>
* @type {?string}
*/
this.key = process.env.TMIO_API;
} else {
this.key = null;
}
}
}
/**
* The default API Paths options.
*/
class defaultOptionsAPIPaths {
constructor(defaultOptionsAPI) {
/**
* The default API options.
* @type {defaultOptionsAPI}
* @private
* @readonly
*/
this._defaultOptionsAPI = defaultOptionsAPI;
/**
* The default Trackmania.io Paths options.
* @type {defaultOptionsAPIPathsTMIO}
*/
this.tmio = new defaultOptionsAPIPathsTMIO();
/**
* The default Trackmania.exchange Paths options.
* @type {defaultOptionsAPIPathsTMX}
*/
this.tmx = new defaultOptionsAPIPathsTMX();
}
}
/**
* The default Trackmania.io Paths options.
*/
class defaultOptionsAPIPathsTMIO {
constructor(defaultOptionsAPIPaths) {
/**
* The default API Paths options.
* @type {defaultOptionsAPIPaths}
* @private
* @readonly
*/
this._defaultOptionsAPIPaths = defaultOptionsAPIPaths;
/**
* The default Trackmania.io Protocol to use.
* @type {string}
*/
this.protocol = "https";
/**
* The default Trackmania.io Host to use.
* @type {string}
*/
this.host = "trackmania.io";
/**
* The default Trackmania.io API Route to use.
* @type {string}
*/
this.api = "api";
/**
* The default Trackmania.io API Tabs to use.
* @type {defaultOptionsAPIPathsTMIOTabs}
*/
this.tabs = new defaultOptionsAPIPathsTMIOTabs();
}
}
/**
* The default Trackmania.io Tabs options.
*/
class defaultOptionsAPIPathsTMIOTabs {
constructor(defaultOptionsAPIPathsTMIO) {
/**
* The default Trackmania.io API options.
* @type {defaultOptionsAPIPathsTMIO}
* @private
* @readonly
*/
this._defaultOptionsAPIPathsTMIO = defaultOptionsAPIPathsTMIO;
/**
* TOTD Tab.
* @type {string}
*/
this.totd = "totd";
/**
* COTD Tab.
* @type {string}
*/
this.cotd = "cotd";
/**
* Competitions Tab.
* @type {string}
*/
this.comp = "comp";
/**
* News Tab.
* @type {string}
*/
this.news = "splashscreens";
/**
* Ads tab (Maniapub)
* @type {string}
*/
this.ads = "ads";
/**
* Campaigns Tab.
* @type {string}
*/
this.campaigns = "campaigns";
/**
* Specific campaign Tab.
* @type {string}
*/
this.campaign = "campaign";
/**
* Official campaign Tab.
* @type {string}
*/
this.officialCampaign = "officialcampaign";
/**
* Rooms Tab.
* @type {string}
*/
this.rooms = "rooms";
/**
* Specific room Tab.
* @type {string}
*/
this.room = "room";
/**
* Clubs Tab.
* @type {string}
*/
this.clubs = "clubs";
/**
* Specific club Tab.
* @type {string}
*/
this.club = "club";
/**
* Members Tab.
* @type {string}
*/
this.members = "members";
/**
* Activities Tab.
* @type {string}
*/
this.activities = "activities";
/**
* Activity Tab.
* @type {string}
*/
this.activity = "activity";
/**
* Events Tab.
* @type {string}
*/
this.events = "competitions";
/**
* Specific player Tab.
* @type {string}
*/
this.player = "player";
/**
* Players Tab.
* @type {string}
*/
this.players = "players";
/**
* Trophies Tab.
* @type {string}
*/
this.trophies = "trophies";
/**
* Top Trophies Tab.
* @type {string}
*/
this.topTrophies = "top/trophies";
/**
* Leaderboard Tab.
* @type {string}
*/
this.leaderboard = "leaderboard";
/**
* Map Tab.
* @type {string}
*/
this.map = "map";
/**
* Matches Tab.
* @type {string}
*/
this.matches = "matches";
/**
* Matchmaking Tab.
* @type {string}
*/
this.topMatchmaking = "top/matchmaking";
/**
* Specific match Tab.
* @type {string}
*/
this.match = "match";
/**
* Specific challenge Tab.
* @type {string}
*/
this.challenge = "challenge";
}
}
/**
* The default Trackmania.exchange Paths options.
*/
class defaultOptionsAPIPathsTMX {
constructor(defaultOptionsAPIPaths) {
/**
* The default API Paths options.
* @type {defaultOptionsAPIPaths}
* @private
* @readonly
*/
this._defaultOptionsAPIPaths = defaultOptionsAPIPaths;
/**
* The default Trackmania.exchange Protocol to use.
* @type {string}
*/
this.protocol = "https";
/**
* The default Trackmania.exchange Host to use.
* @type {string}
*/
this.host = "trackmania.exchange";
/**
* The default Trackmania.exchange API Route to use.
* @type {string}
*/
this.api = "api";
/**
* The default Trackmania.exchange API Tabs to use.
* @type {defaultOptionsAPIPathsTMXTabs}
*/
this.tabs = new defaultOptionsAPIPathsTMXTabs();
}
}
/**
* The default Trackmania.exchange Tabs options.
*/
class defaultOptionsAPIPathsTMXTabs {
constructor(defaultOptionsAPIPathsTMX) {
/**
* The default Trackmania.exchange API options.
* @type {defaultOptionsAPIPathsTMX}
* @private
* @readonly
*/
this._defaultOptionsAPIPathsTMX = defaultOptionsAPIPathsTMX;
/**
* Map Info Tab.
* @type {string}
*/
this.mapInfo = "maps/get_map_info/multi";
/**
* Map Download Tab.
* @type {string}
*/
this.mapsDownload = "maps/download";
}
}
/**
* The default Cache options.
*/
class defaultOptionsCache {
constructor(defaultOptions) {
/**
* The default Options.
* @type {defaultOptions}
* @private
* @readonly
*/
this._defaultOptions = defaultOptions;
/**
* Whether to cache the API responses.
* @type {boolean}
*/
this.enabled = true;
/**
* The cache TTL in minutes.
* @type {number}
*/
this.ttl = 10;
/**
* The cache TTL for leaderboards in minutes.
* @type {number}
*/
this.leaderboardttl = 1;
/**
* The cache TTL for rooms in minutes.
* @type {number}
*/
this.roomttl = 5;
}
}
module.exports = defaultOptions;

31
packages/bridge-shared/trackmania.io-f/test/campaigns.test.js

@ -0,0 +1,31 @@
require('dotenv').config();
const assert = require('assert'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("Campaigns", function(){
this.timeout(15*1000);
describe("Official campaign", function(){
it("Current season is official", async function(){
const currentSeason = await tmioClient.campaigns.currentSeason();
assert.equal(currentSeason.isOfficial, true);
});
it("Fall 2020", async function(){
const campaigns = await tmioClient.campaigns.officialCampaigns();
const campaign = await campaigns.find(c=>c.id == 4791).getCampaign();
assert.equal(campaigns.find(c=>c.id == 4791).mapCount, 25);
assert.equal(campaign.isOfficial, true);
});
});
it("Club campaign", async function(){
const campaign = await tmioClient.campaigns.get(10, 25);
assert.equal(campaign.isOfficial, false);
});
});

30
packages/bridge-shared/trackmania.io-f/test/clubs.test.js

@ -0,0 +1,30 @@
require('dotenv').config();
const assert = require('assert'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("Clubs", function(){
this.timeout(15*1000);
it("Search clubs", async function(){
const results = await tmioClient.clubs.search("Openplanet");
assert.equal(results.length > 0, true);
assert.equal(results.some(c=>c.id == 9), true);
});
it("Club Info", async function(){
const club = await tmioClient.clubs.get(23500),
creator = await club.creator();
assert.equal(creator.id, "26d9a7de-4067-4926-9d93-2fe62cd869fc");
assert.equal(club.createdAt.getTime(), 1614627947000);
});
it("Club Members", async function(){
const club = await tmioClient.clubs.get(54),
members = await club.fetchMembers();
assert.equal(members.length > 0, true);
assert.equal(members[0].isCreator, true);
});
});

15
packages/bridge-shared/trackmania.io-f/test/events.test.js

@ -0,0 +1,15 @@
require('dotenv').config();
const assert = require('assert'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("Events", function(){
this.timeout(15*1000);
it("Competition info", async function(){
const comp = await tmioClient.events.get(1165);
assert.equal(comp.name, "TMGL Fall 2021");
assert.equal(comp.liveId, "LID-COMP-2ess1ztbj42pefb");
});
});

22
packages/bridge-shared/trackmania.io-f/test/map.test.js

@ -0,0 +1,22 @@
require('dotenv').config();
const assert = require('assert'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("Maps", function(){
this.timeout(15*1000);
it("Map Info", async function(){
const map = await tmioClient.maps.get('1jEeZQTADlb9wIY2YzCjZ5Lpmxh');
const author = await map.author();
const exchange = await map.exchange();
assert.equal(author.login, "Jtmn3kBnSSadky_mLNhp_A");
assert.equal(map.medalTimes.author, 11216);
assert.equal(map.medalTimes.gold, 12000);
assert.equal(map.medalTimes.silver, 14000);
assert.equal(map.medalTimes.bronze, 17000);
assert.equal(exchange.id, 41427);
});
});

65
packages/bridge-shared/trackmania.io-f/test/players.test.js

@ -0,0 +1,65 @@
require('dotenv').config();
const assert = require('assert'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("Players", function(){
this.timeout(15*1000);
describe("Player info", function(){
it("Greep", async function(){
const player = await tmioClient.players.get("greep");
assert.equal(player.id, "26d9a7de-4067-4926-9d93-2fe62cd869fc", "Wrong account ID");
assert.equal(player.meta.inNadeo, false);
assert.equal(player.meta.inTMIOTeam, false);
});
it("Hylis", async function(){
const player = await tmioClient.players.get("hylis");
assert.equal(player.id, "2232c721-f215-4036-b28b-772eee46632c", "Wrong account ID");
assert.equal(player.meta.inNadeo, true);
assert.equal(player.meta.inTMIOTeam, false);
});
it("Miss", async function(){
const player = await tmioClient.players.get("miss");
assert.equal(player.id, "7398eeb6-9b4e-44b8-a7a1-a2149955ac70", "Wrong account ID");
assert.equal(player.meta.inNadeo, false);
assert.equal(player.meta.inTMIOTeam, true);
});
it("Gwen", async function(){
const player = await tmioClient.players.get("gwen");
assert.equal(player.id, "dba55c7e-d5cd-40c0-a5e7-8e793fd295eb", "Wrong account ID");
assert.equal(player.meta.inTMGL, true);
assert.equal(player.meta.inTMIOTeam, false);
})
});
describe("Player search", function(){
it("Test 1", async function(){
const results = await tmioClient.players.search("usefiujnskxdfhousdhfjefojsd");
assert.equal(results.length, 0);
});
it("Test 2", async function(){
const results = await tmioClient.players.search("greep");
assert.equal(results.length > 0, true);
assert.equal(results[0].id, "26d9a7de-4067-4926-9d93-2fe62cd869fc");
});
});
describe("Player groups", function(){
it("Nadeo", async function(){
const group = await tmioClient.players.group("nadeo");
assert.equal(group.some(p=>p.id == "2232c721-f215-4036-b28b-772eee46632c"), true, "Hylis not found");
assert.equal(group.some(p=>p.id == "a76653e1-998a-4c53-8a91-0a396e15bfb5"), true, "Darrek not found");
});
it("Team", async function(){
const group = await tmioClient.players.group("team");
assert.equal(group.some(p=>p.id == "7398eeb6-9b4e-44b8-a7a1-a2149955ac70"), true, "Miss not found");
assert.equal(group.some(p=>p.id == "5b4d42f4-c2de-407d-b367-cbff3fe817bc"), true, "tooInfinite not found");
});
});
});

24
packages/bridge-shared/trackmania.io-f/test/rooms.test.js

@ -0,0 +1,24 @@
require('dotenv').config();
const assert = require('assert'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("Rooms", function(){
this.timeout(15*1000);
it("Dedicated", async function(){
const room = await tmioClient.rooms.get(41, 769);
assert.equal(room.isCloud, false);
assert.equal(room.region, null);
assert.equal(room.login, "evoiceb");
});
it("Hosted Room", async function(){
const room = await tmioClient.rooms.get(15, 1476);
assert.equal(room.isCloud, true);
assert.equal(room.region, "eu-west");
assert.equal(room.login, null);
});
});

30
packages/bridge-shared/trackmania.io-f/test/totd.test.js

@ -0,0 +1,30 @@
require('dotenv').config();
const assert = require('assert'),
{DateTime} = require('luxon'),
TMIO = require('../'),
tmioClient = new TMIO.Client({dev: true});
describe("TOTD", function(){
this.timeout(15*1000);
it("TOTD info - Plinko - 2021-04-01", async function(){
const date = new Date(new Date().setFullYear(2021, 3, 1)),
totd = await tmioClient.totd.get(date),
map = await totd.map(),
author = await map.author();
assert.equal(totd.leaderboardId, "bf597bc1-a8c9-4dfa-9ae9-40a3a5e4a0bf");
assert.equal(totd.campaignId, 8383);
assert.equal(map.uid, "17B5XtQBJ_nukdrylfT7Pj1q1C1");
assert.equal(map.exchangeId, 23641);
assert.equal(author.id, "62c59cd2-4981-43cc-a6d2-7feaf96ceeb1");
});
it("TOTD dates", async function(){
const date = DateTime.local().setZone("Europe/Paris"),
totd = await tmioClient.totd.get(date.toJSDate());
assert.equal(totd.month, date.month);
assert.equal(totd.year, date.year);
});
});

9
packages/bridge-shared/trackmania.io-f/tsconfig.json

@ -0,0 +1,9 @@
{
"include": ["src/**/*"],
"compilerOptions": {
"allowJs": true,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "typings"
}
}

74
packages/bridge-shared/trackmania.io-f/typings/client/BaseClient.d.ts

@ -0,0 +1,74 @@
export = BaseClient;
/**
* The Base Client
* @extends {EventEmitter}
*/
declare class BaseClient {
constructor(options?: {});
/**
* The options of the client.
* @type {defaultOptions}
* @readonly
*/
readonly options: defaultOptions;
/**
* Get the ratelimits details on trackmania.io.
* @type {ClientRatelimit}
*/
ratelimit: ClientRatelimit;
/**
* Do an API request
* @param {string} url The URL to request
* @param {string} [method="GET"] The HTTP method to use
* @param {?Object} [body=null] The data to send
* @returns {Promise<Object>} A promise that resolves to the API response
* @private
*/
private _apiReq;
/**
* Sets a User Agent for your project.
* Required if you run a important project.
* @param {string} [useragent=this.options.api.useragent] The User Agent to set to.
* @returns {void}
*/
setUserAgent(useragent?: string): void;
/**
* Sets an API Key.
* Required if you run a important project. It will triple the rate limit.
*
* <warn>This should be kept private at all times.</warn>
* @param {string} [key=this.options.api.key] The full API key. It must contains "yourname:theactualsecretkey"
* @returns {void}
*/
setAPIKey(key?: string): void;
}
import defaultOptions = require("../util/defaultOptions");
/**
* The ratelimit details of the client.
*/
declare class ClientRatelimit {
constructor(baseClient: any);
/**
* The base client.
* @type {BaseClient}
*/
baseClient: BaseClient;
/**
* The total number of requests you can make on trackmania.io API.
* If null, it means you haven't actually done a request
* @type {?number}
*/
ratelimit: number | null;
/**
* The number of requests you can make before the ratelimit resets.
* If null, it means you haven't actually done a request
* @type {?number}
*/
remaining: number | null;
/**
* The date when the ratelimit resets.
* If null, it means you haven't actually done a request
* @type {?Date}
*/
reset: Date | null;
}

92
packages/bridge-shared/trackmania.io-f/typings/client/Client.d.ts

@ -0,0 +1,92 @@
export = Client;
/**
* Instantiates a new client. This is the entry point.
* @extends {BaseClient}
*/
declare class Client extends BaseClient {
/**
* @param {defaultOptions} [options={}] The options to use
*/
constructor(options?: defaultOptions);
/**
* The player manager
* @type {PlayerManager}
*/
players: PlayerManager;
/**
* The map manager
* @type {MapManager}
*/
maps: MapManager;
/**
* The TOTD manager
* @type {TOTDManager}
*/
totd: TOTDManager;
/**
* The COTD manager
* @type {COTDManager}
*/
cotd: COTDManager;
/**
* The club manager
* @type {ClubManager}
*/
clubs: ClubManager;
/**
* The campaign manager
* @type {CampaignManager}
*/
campaigns: CampaignManager;
/**
* The room manager
* @type {RoomManager}
*/
rooms: RoomManager;
/**
* The TM events manager
* @type {EventManager}
*/
events: EventManager;
/**
* The matches manager
* @type {MatchesManager}
*/
matches: MatchesManager;
/**
* The news manager
* @type {NewsManager}
*/
news: NewsManager;
/**
* The Maniapub manager
* @type {AdsManager}
*/
ads: AdsManager;
/**
* Format the string and remove the TM style code on it.
* @param {string} str string to format
* @returns {string}
* @deprecated use {@link Client#stripFormat} instead
*/
formatTMText(str: string): string;
/**
* Format the string and remove the TM style code on it.
* @param {string} str string to format
* @returns {string}
*/
stripFormat(str: string): string;
}
import BaseClient = require("./BaseClient");
import PlayerManager = require("../managers/PlayerManager");
import MapManager = require("../managers/MapManager");
import TOTDManager = require("../managers/TOTDManager");
import COTDManager = require("../managers/COTDManager");
import ClubManager = require("../managers/ClubManager");
import CampaignManager = require("../managers/CampaignManager");
import RoomManager = require("../managers/RoomManager");
import EventManager = require("../managers/EventManager");
import MatchesManager = require("../managers/MatchesManager");
import NewsManager = require("../managers/NewsManager");
import AdsManager = require("../managers/AdsManager");
import defaultOptions = require("../util/defaultOptions");

1
packages/bridge-shared/trackmania.io-f/typings/index.d.ts

@ -0,0 +1 @@
export let Client: typeof import("./client/Client");

46
packages/bridge-shared/trackmania.io-f/typings/managers/AdsManager.d.ts

@ -0,0 +1,46 @@
export = AdsManager;
/**
* Represents a in-game ads manager (also called Maniapub).
*/
declare class AdsManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Get the in-game Maniapub list
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the ads or not
* @returns {Promise<Array<Ad>>}
*/
list(cache?: boolean): Promise<Array<Ad>>;
/**
* Fetches a Trackmania Maniapub and returns its data.
* @param {string} adUid The Ad UID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the news from cache or not
* @returns {Promise<Ad>} The Maniapub
* @example
* client.ads.get("fcc22a11-0d59-4fef-b102-0bf5a2df7221").then(ad => {
* console.log(ad.name);
* });
*/
get(adUid: string, cache?: boolean): Promise<Ad>;
/**
* Fetches a maniapub and returns its data
* @param {number} adUid The ad ID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Splashscreen>} The splashscreen
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import Ad = require("../structures/Ad");

122
packages/bridge-shared/trackmania.io-f/typings/managers/COTDManager.d.ts

@ -0,0 +1,122 @@
export = COTDManager;
/**
* Represents a COTD Manager.
*/
declare class COTDManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Get the COTD leaderboard by category
* @param {COTDLeaderboardSortGroup} [sort="wins"] The leaderboard sorting
* @param {boolean} [includeReruns=false] Whether to include reruns when sorting or not
* @param {number} [page=0] The page number
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the list from cache or not
* @returns {Promise<Array<COTDLeaderboard>>}
*/
leaderboard(sort?: COTDLeaderboardSortGroup, includeReruns?: boolean, page?: number, cache?: boolean): Promise<Array<COTDLeaderboard>>;
/**
* Fetches the latest COTDs and returns its data
* @param {number} [page=0] The page, each page contains 12 items
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the list from cache or not
* @returns {Promise<Array<COTD>>} The COTD list
* @example
* client.cotd.get().then(event => {
* console.log(event.name);
* });
*/
get(page?: number, cache?: boolean): Promise<Array<COTD>>;
/**
* Fetches a COTD and returns its data
* @param {number} [page=0] The page
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the list or not
* @returns {Promise<Array<COTD>>} The COTD list
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import { COTDLeaderboardSortGroup } from "../util/Constants";
/**
* Represents a position in the COTD Leaderboard
*/
declare class COTDLeaderboard {
constructor(client: any, data: any);
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data of the COTD leaderboard.
* @type {Object}
* @private
*/
private _data;
/**
* The player's name
* @type {string}
*/
get playerName(): string;
/**
* The player's club tag
* @type {string}
*/
get playerTag(): string;
/**
* The player's account ID
* @type {string}
*/
get playerId(): string;
/**
* The player
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
/**
* The position of the player in the selected category
* @type {number}
*/
get position(): number;
/**
* The amount of COTD the player has played
* @type {number}
*/
get played(): number;
/**
* The amount of COTD reruns the player has played
* @type {number}
*/
get rerunsPlayed(): number;
/**
* The amount of COTD the player has won
* @type {number}
*/
get wins(): number;
/**
* The amount of COTD reruns the player has won
* @type {number}
*/
get rerunsWins(): number;
/**
* The amount of win streak the player has
* @type {number}
*/
get winStreak(): number;
/**
* The amount of win streak the player has (reruns included)
* @type {number}
*/
get winStreakWithReruns(): number;
}
import COTD = require("../structures/COTD");
import Player = require("../structures/Player");

39
packages/bridge-shared/trackmania.io-f/typings/managers/CacheManager.d.ts

@ -0,0 +1,39 @@
export = CacheManager;
/**
* The Cache Manager is responsible for managing the cache.
* @extends {Map}
* @private
*/
declare class CacheManager {
constructor(client: any, from: any, to: any);
/**
* The class that instantiated this manager.
* @type {*}
* @readonly
*/
readonly from: any;
/**
* The class this manager will operate on.
* @type {*}
* @readonly
*/
readonly to: any;
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The time to live for the cache in miliseconds.
* @type {number}
* @private
*/
private _ttl;
/**
* Resets the cache based on the ttl.
* @private
* @type {void}
*/
private _reset;
}
import Client = require("../client/Client");

114
packages/bridge-shared/trackmania.io-f/typings/managers/CampaignManager.d.ts

@ -0,0 +1,114 @@
export = CampaignManager;
/**
* Represents a manager for campaigns.
*/
declare class CampaignManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Get the current official campaign
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to use from the cache or not
* @returns {Promise<Campaign>} The campaign
*/
currentSeason(cache?: boolean): Promise<Campaign>;
/**
* Get all official campaigns from recent to old
* @returns {Promise<Array<CampaignSearchResult>>} The campaigns
*/
officialCampaigns(): Promise<Array<CampaignSearchResult>>;
/**
* Get all popular campaigns (official excluded) (50 items / page)
* @param {number} [page=0] The page number
* @returns {Promise<Array<CampaignSearchResult>>} The campaigns
*/
popularCampaigns(page?: number): Promise<Array<CampaignSearchResult>>;
/**
* Searches for a campaign
* @param {string} query The query
* @param {number} [page=0] The page number
* @returns {Promise<Array<CampaignSearchResult>>} The campaigns
* @example
* client.campaigns.search('htimh').then(campaigns => {
* campaigns[0].getCampaign().then(async campaign => {
* const maps = await campaign.maps();
* maps.forEach(map => console.log(map.name));
* });
* });
*/
search(query: string, page?: number): Promise<Array<CampaignSearchResult>>;
/**
* Fetches a Trackmania campaign and returns its data
* @param {number} clubId The club Id that the campaign belongs to (If it's an official campaign, set it to 0)
* @param {number} id The campaign Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the campaign from cache or not
* @returns {Promise<Campaign>} The campaign
* @example
* client.campaigns.get(54, 10621).then(campaign => {
* console.log(campaign.name);
* });
*/
get(clubId: number, id: number, cache?: boolean): Promise<Campaign>;
/**
* Fetches a campaign and returns its data
* @param {number} clubId The club Id that the campaign belongs to
* @param {string} id The campaign Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the campaign or not
* @returns {Campaign} The campaign
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import Campaign = require("../structures/Campaign");
/**
* The result of a campaign search. It is completely different from the {@link Campaign} object.
*/
declare class CampaignSearchResult {
constructor(client: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The campaign's ID
* @type {number}
*/
id: number;
/**
* The campaign's Club ID
* @type {number}
*/
clubId: number;
/**
* The campaign's name
* @type {string}
*/
name: string;
/**
* The campaign's creation date
* @type {Date}
*/
date: Date;
/**
* The campaign's map count
* @type {number}
*/
mapCount: number;
/**
* Return to the Campaign Object
* @returns {Promise<Campaign>}
*/
getCampaign(): Promise<Campaign>;
}

54
packages/bridge-shared/trackmania.io-f/typings/managers/ClubManager.d.ts

@ -0,0 +1,54 @@
export = ClubManager;
/**
* Represents a manager for clubs.
*/
declare class ClubManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Gets all the popular clubs
* @param {number} [page=0] The page number
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the clubs or not
* @returns {Promise<Array<Club>>} The clubs
*/
popularClubs(page?: number, cache?: boolean): Promise<Array<Club>>;
/**
* Searches for a club
* @param {string} query Search query
* @param {number} [page=0] The page number
* @returns {Promise<Array<Club>>}
*/
search(query: string, page?: number): Promise<Array<Club>>;
/**
* Fetches a Trackmania Club and returns its data
* @param {number} id The Club Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the club from cache or not
* @returns {Promise<Club>} The Club
* @example
* client.clubs.get(54).then(club => {
* console.log(club.name);
* });
*/
get(id: number, cache?: boolean): Promise<Club>;
/**
* Fetches a map and returns its data
* @param {string} id The Club Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the club or not
* @returns {Club} The club
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import Club = require("../structures/Club");

94
packages/bridge-shared/trackmania.io-f/typings/managers/EventManager.d.ts

@ -0,0 +1,94 @@
export = EventManager;
/**
* Represents a manager for in-game events.
*/
declare class EventManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* List all available events by creation date
* @param {number} [page=0] The page number
* @returns {Promise<Array<EventSearchResult>>} The events
*/
listEvents(page?: number): Promise<Array<EventSearchResult>>;
/**
* Searches for an event by name
* @param {string} query The query
* @param {number} [page=0] The page number
* @returns {Promise<Array<EventSearchResult>>} The events
*/
search(query: string, page?: number): Promise<Array<EventSearchResult>>;
/**
* Fetches a Trackmania event and returns its data
* @param {number} eventId The event id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the map from cache or not
* @returns {Promise<TMEvent>} The event
* @example
* client.events.get(706).then(event => {
* console.log(event.name);
* });
*/
get(eventId: number, cache?: boolean): Promise<TMEvent>;
/**
* Fetches a event and returns its data
* @param {number} eventId The event id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the map or not
* @returns {Event} The event
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
/**
* The result of a campaign search. It is completely different from the {@link TMEvent} object.
*/
declare class EventSearchResult {
constructor(client: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The event's ID
* @type {number}
*/
id: number;
/**
* The event's competiton ID
* @type {number}
*/
compId: number;
/**
* The event's Club ID
* @type {number}
*/
clubId: number;
/**
* The event's name
* @type {string}
*/
name: string;
/**
* The event's creation date
* @type {Date}
*/
date: Date;
/**
* Return to the TMEvent Object
* @returns {Promise<TMEvent>}
*/
event(): Promise<TMEvent>;
}
import TMEvent = require("../structures/TMEvent");

40
packages/bridge-shared/trackmania.io-f/typings/managers/MapManager.d.ts

@ -0,0 +1,40 @@
export = MapManager;
/**
* Represents a map manager.
*/
declare class MapManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Fetches a Trackmania map and returns its data
* @param {string} mapUid The map UID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the map from cache or not
* @returns {Promise<TMMap>} The map
* @example
* client.maps.get('z28QXoFnpODEGgg8MOederEVl3j').then(map => {
* console.log(map.name);
* });
*/
get(mapUid: string, cache?: boolean): Promise<TMMap>;
/**
* Fetches a map and returns its data
* @param {string} mapUid The map UID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the map or not
* @returns {TMMap} The map
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import TMMap = require("../structures/TMMap");

82
packages/bridge-shared/trackmania.io-f/typings/managers/MatchesManager.d.ts

@ -0,0 +1,82 @@
export = MatchesManager;
/**
* Represents the matches manager (3v3 or Royal matches).
*/
declare class MatchesManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Get a list of the matches
* @param {MatchmakingGroup} [group='3v3'] The group to get the matches from
* @param {number} [page=0] The page to get
* @param {Boolean} [cache=this.client.options.cache.enabled] Whether to cache the matches or not
* @returns {Promise<Array<MatchResult>>}
*/
list(group?: MatchmakingGroup, page?: number, cache?: boolean): Promise<Array<MatchResult>>;
/**
* Fetches a Trackmania Match and returns its data.
* @param {string} liveID The live ID of the match (should start with 'LID-MTCH')
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the news from cache or not
* @returns {Promise<Match>} The Match
*/
get(liveID: string, cache?: boolean): Promise<Match>;
/**
* Fetches a Match and returns its data
* @param {string} liveID Thelive ID of the match (should start with 'LID-MTCH')
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Match>} The splashscreen
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import { MatchmakingGroup } from "../util/Constants";
/**
* The result of a Match from the list. It is completely different from the {@link Match} object.
*/
declare class MatchResult {
constructor(client: any, data: any);
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The match ID
* @type {number}
*/
id: number;
/**
* The match Live ID
* @type {string}
*/
liveID: string;
/**
* The match start date
* @type {Date}
*/
startDate: Date;
/**
* The status of the match
* @type {MatchStatus}
*/
status: MatchStatus;
/**
* Get the match
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the match or not
*/
match(cache?: boolean): Promise<Match>;
}
import Match = require("../structures/Match");
import { MatchStatus } from "../util/Constants";

47
packages/bridge-shared/trackmania.io-f/typings/managers/NewsManager.d.ts

@ -0,0 +1,47 @@
export = NewsManager;
/**
* Represents a in-game news manager.
*/
declare class NewsManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Get the in-game news list
* @param {number} page The page number
* @param {Boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Array<Splashscreen>>}
*/
list(page?: number, cache?: boolean): Promise<Array<Splashscreen>>;
/**
* Fetches a Trackmania splashscreen and returns its data.
* @param {number} newsId The splashscreen ID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the news from cache or not
* @returns {Promise<Splashscreen>} The splashscreen
* @example
* client.news.get(143).then(news => {
* console.log(news.title);
* });
*/
get(newsId: number, cache?: boolean): Promise<Splashscreen>;
/**
* Fetches a splashscreen and returns its data
* @param {number} newsId The splashscreen ID
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the news or not
* @returns {Promise<Splashscreen>} The splashscreen
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import Splashscreen = require("../structures/Splashscreen");

180
packages/bridge-shared/trackmania.io-f/typings/managers/PlayerManager.d.ts

@ -0,0 +1,180 @@
export = PlayerManager;
/**
* Represents a manager for players.
*/
declare class PlayerManager {
constructor(client: any);
/**
* The client instance
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Returns the login of an account ID
* @param {string} accountID The account ID
* @returns {string}
*/
toLogin(accountID: string): string;
/**
* Returns the Account ID of a login
* @param {string} login The login of the player
* @returns {?string}
*/
toAccountId(login: string): string | null;
/**
* Searches for a player by its name
* @param {string} query The query to search for
* @returns {Promise<Array<PlayerSearchResult>>} The results
* @example
* // Search for a player
* client.players.search('greep').then(results => {
* client.players.get(results[0].id).then(player => {
* console.log('The tag of this player is', player.tag);
* });
* });
*/
search(query: string): Promise<Array<PlayerSearchResult>>;
/**
* Get all players from a group
* @param {PlayerGroup} groupName The group name
* @returns {?Promise<Array<PlayerSearchResult>>} The results
*/
group(groupName: PlayerGroup): Promise<Array<PlayerSearchResult>> | null;
/**
* Get the trophy leaderboard
* @param {number} [page=0] The page number
* @returns {Promise<Array<PlayerTopTrophy>>} The players' top trophies
* @example
* Client.players.topTrophies().then(top => {
* console.log("The number 1 player is " + top[0].player.name + " with " + top[0].score + " trophies");
* });
*/
topTrophies(page?: number): Promise<Array<PlayerTopTrophy>>;
/**
* Gets the matchmaking leaderboard
* @param {MatchmakingGroup} group The matchmaking group
* @param {number} [page=0] The page number
* @returns {Promise<Array<PlayerTopMatchmaking>>} The players' top matchmaking
*/
topMatchmaking(group: MatchmakingGroup, page?: number): Promise<Array<PlayerTopMatchmaking>>;
/**
* Fetches a player and returns its data
* @param {string} accountId The account ID or its tm.io vanity name
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the player from cache or not
* @returns {Promise<Player>} The player
* @example
* // Get a player
* client.players.get('26d9a7de-4067-4926-9d93-2fe62cd869fc').then(player => {
* console.log(player.name);
* });
*/
get(accountId: string, cache?: boolean): Promise<Player>;
/**
* Fetches a player and returns its data
* @param {string} accountId The account ID or its tm.io vanity name
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the player or not
* @returns {Player} The player
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
/**
* The result of a player search. It is completely different from the {@link Player} object.
*/
declare class PlayerSearchResult {
constructor(client: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The player's account ID
* @type {string}
*/
id: string;
/**
* The player's display name
* @type {string}
*/
name: string;
/**
* The player's club tag (if any)
* @type {?string}
*/
tag: string | null;
/**
* Return to the Player Object
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
}
import { PlayerGroup } from "../util/Constants";
/**
* Represents a player top trophy
*/
declare class PlayerTopTrophy {
constructor(client: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The player
* @type {PlayerSearchResult}
*/
player: PlayerSearchResult;
/**
* The rank
* @type {number}
*/
rank: number;
/**
* The score (number of trophies)
* @type {number}
*/
score: number;
}
import { MatchmakingGroup } from "../util/Constants";
/**
* The player top matchmaking
*/
declare class PlayerTopMatchmaking {
constructor(client: any, typeId: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The player
* @type {PlayerSearchResult}
*/
player: PlayerSearchResult;
/**
* The rank
* @type {number}
*/
rank: number;
/**
* The score
* @type {number}
*/
score: number;
/**
* The matchmaking division of the player
* @type {MatchmakingDivision}
*/
division: MatchmakingDivision;
}
import Player = require("../structures/Player");
import MatchmakingDivision = require("../structures/MatchmakingDivision");

101
packages/bridge-shared/trackmania.io-f/typings/managers/RoomManager.d.ts

@ -0,0 +1,101 @@
export = RoomManager;
/**
* Represents a manager for rooms.
*/
declare class RoomManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Get the popular Club rooms (by number of players connected)
* @param {number} [page=0] The page number
* @returns {Promise<Array<RoomSearchResult>>} The rooms
*/
popularRooms(page?: number): Promise<Array<RoomSearchResult>>;
/**
* Searches for a room
* @param {string} query The query to search for
* @param {number} [page=0] The page number
* @returns {Promise<Array<RoomSearchResult>>} The rooms
*/
search(query: string, page?: number): Promise<Array<RoomSearchResult>>;
/**
* Fetches a Trackmania room (server) and returns its data
* @param {number} clubId The club Id that the room belongs to
* @param {number} id The room Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the room from cache or not
* @returns {Promise<Room>} The room
* @example
* client.rooms.get(338, 1180).then(room => {
* console.log(room.name);
* });
*/
get(clubId: number, id: number, cache?: boolean): Promise<Room>;
/**
* Fetches a room and returns its data
* @param {number} clubId The club Id that the room belongs to
* @param {string} id The room Id
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the room or not
* @returns {Campaign} The room
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
/**
* The result of a campaign search. It is completely different from the {@link Room} object.
*/
declare class RoomSearchResult {
constructor(client: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The room's ID
* @type {number}
*/
id: number;
/**
* The room's Club ID
* @type {number}
*/
clubId: number;
/**
* The room's name
* @type {string}
*/
name: string;
/**
* Whether the room is hosted by Nadeo
* @type {boolean}
*/
nadeoHosted: boolean;
/**
* The player count
* @type {number}
*/
playerCount: number;
/**
* The max player count
* @type {number}
*/
maxPlayerCount: number;
/**
* Return to the Room Object
* @returns {Promise<Room>}
*/
room(): Promise<Room>;
}
import Room = require("../structures/Room");

47
packages/bridge-shared/trackmania.io-f/typings/managers/TOTDManager.d.ts

@ -0,0 +1,47 @@
export = TOTDManager;
/**
* Represents a manager for TOTDs.
*/
declare class TOTDManager {
constructor(client: any);
/**
* The client instance.
* @type {Client}
* @readonly
*/
readonly client: Client;
/**
* The cache manager
* @type {CacheManager}
* @private
*/
private _cache;
/**
* Calculate the number of months between today and the month and year
* @param {Date} date The date
* @private
*/
private _calculateMonths;
/**
* Fetches a TOTD with it's day and returns its data
* @param {Date} date The date
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the map from cache or not
* @returns {Promise<TOTD>} The map
* @example
* // Gets the TOTD of today
* client.totd.get(new Date()).then(totd => {
* console.log(totd.map.name);
* });
*/
get(date: Date, cache?: boolean): Promise<TOTD>;
/**
* Fetches a TOTD and returns its data
* @param {Date} date The date
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the map or not
* @returns {TOTD} The map
* @private
*/
private _fetch;
}
import Client = require("../client/Client");
import TOTD = require("../structures/TOTD");

42
packages/bridge-shared/trackmania.io-f/typings/rest/APIRequest.d.ts

@ -0,0 +1,42 @@
export = APIRequest;
/**
* APIRequest class
* @private
*/
declare class APIRequest {
/**
* @param {BaseClient} client The client object
*/
constructor(client: BaseClient);
/**
* The client that created this request
* @type {BaseClient}
* @readonly
*/
readonly client: BaseClient;
/**
* The User-Agent string which is used for the requests
* @type {string}
*/
UA: string;
/**
* The API key for the requests on trackmania.io
* @type {string}
*/
key: string;
/**
* Makes a request to the API
* @param {string} url The URL
* @param {string} [method="GET"] The method
* @param {Object} [body=null] The body
* @returns {Promise<Object>} The response
*/
do(url: string, method?: string, body?: any): Promise<any>;
url: string;
options: {
headers: any;
method: string;
body: any;
};
}
import BaseClient = require("../client/BaseClient");

64
packages/bridge-shared/trackmania.io-f/typings/structures/Ad.d.ts

@ -0,0 +1,64 @@
export = Ad;
/**
* The Maniapub
*/
declare class Ad {
constructor(client: any, data: any);
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The Ad UID
* @type {string}
*/
get uid(): string;
/**
* The name of the Ad
* @type {string}
*/
get name(): string;
/**
* The type of the Ad
* @type {AdType}
*/
get type(): string;
/**
* The URL linked with this ad
* @type {string}
*/
get url(): string;
/**
* The 2x3 image URL of this ad (shown on the vertical screen)
* @type {string}
*/
get verticalImage(): string;
/**
* The 16x9 image URL of this ad (shown on the big screen)
* @type {string}
*/
get image(): string;
/**
* The 64x10 image URL of this ad (shown on Start, CPs and Finish)
* @type {string}
*/
get cpImage(): string;
/**
* The media of this ad (mostly the vertical image)
* @type {string}
*/
get media(): string;
/**
* The display format of this screen (2x3 vertical image by default)
* @type {string}
*/
get displayFormat(): string;
}
import Client = require("../client/Client");

50
packages/bridge-shared/trackmania.io-f/typings/structures/COTD.d.ts

@ -0,0 +1,50 @@
export = COTD;
/**
* Represents a COTD event.
*/
declare class COTD {
constructor(client: any, data: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The event associated on this COTD
* @returns {Promise<TMEvent>}
*/
getEvent(): Promise<TMEvent>;
/**
* The COTD identifier
* @type {number}
*/
get id(): number;
/**
* The COTD name
* @type {string}
*/
get name(): string;
/**
* The number of players in this COTD
* @type {number}
*/
get playerCount(): number;
/**
* The start date of this COTD
* @type {Date}
*/
get startDate(): Date;
/**
* The end date of this COTD
* @type {Date}
*/
get endDate(): Date;
}
import Client = require("../client/Client");
import TMEvent = require("./TMEvent");

289
packages/bridge-shared/trackmania.io-f/typings/structures/Campaign.d.ts

@ -0,0 +1,289 @@
export = Campaign;
/**
* The Campaign class represents a campaign.
*/
declare class Campaign {
constructor(client: any, data: any);
/**
* The client object of the campaign.
* @type {Client}
*/
client: Client;
/**
* The data object
* @type {Object}
* @private
*/
private _data;
/**
* The leaderboard of the campaign
* @type {Array<CampaignLeaderboard>}
*/
leaderboard: Array<CampaignLeaderboard>;
/**
* The id of the campaign.
* @type {number}
*/
get id(): number;
/**
* The name of the campaign.
* @type {string}
*/
get name(): string;
/**
* Whether the campaign is official.
* @type {boolean}
*/
get isOfficial(): boolean;
/**
* The image URL of the campaign. If this is an official campaign, the decal image URL is returned.
* @type {string}
*/
get image(): string;
/**
* The creation date of the campaign.
* @type {Date}
*/
get createdAt(): Date;
/**
* The last update date of the campaign.
* @type {Date}
*/
get updatedAt(): Date;
/**
* The club that owns the campaign. (if it's not an official campaign)
* @returns {?Promise<Club>}
*/
club(): Promise<Club> | null;
/**
* The leaderboard id of the campaign.
* @type {string}
*/
get leaderboardId(): string;
/**
* Get the number of maps in the campaign.
* @type {number}
*/
get mapCount(): number;
/**
* Get a specific map of the campaign.
* @param {number} index The index of the map.
* @returns {Promise<TMMap>}
*/
map(index: number): Promise<TMMap>;
/**
* The list of maps in the campaign.
* @returns {Promise<Array<TMMap>>}
* @example
* Client.campaigns.get(0, 11612).then(async campaign => {
* const maps = await campaign.maps();
* maps.forEach(map => console.log(map.name));
* });
*/
maps(): Promise<Array<TMMap>>;
/**
* Load more results in the leaderboard.
* @param {number} [nbOfResults=100] The number of results to load. (max 100)
* @returns {Promise<?Array<CampaignLeaderboard>>}
*/
leaderboardLoadMore(nbOfResults?: number): Promise<Array<CampaignLeaderboard> | null>;
/**
* The media images of the campaign, if this is an official campaign.
* @type {?CampaignMedia}
*/
get media(): CampaignMedia;
/**
* Whether the campaign is tracked.
* @type {boolean}
*/
get isTracked(): boolean;
/**
* Gets the campaign activity.
* <info>{@link Campaign#isTracked} must be true.</info>
* @returns {Promise<Array<CampaignRecordActivity>>}
*/
activity(page?: number): Promise<Array<CampaignRecordActivity>>;
/**
* Subscribe to the campaign WR updates.
* <info>{@link Campaign#isTracked} must be true.</info>
* <info>When a new WR is set, the event {@link Campaign#e-wr} will be fired</info>
* @returns {Promise<void>}
* @example
* Client.campaigns.currentSeason().then(campaign => {
* campaign.subWR();
* campaign.on('wr', (map, record) => {
* console.log(`New WR on ${campaign.name} in ${map.name} is ${record.playerName} (${record.time})`);
* });
* });
*/
subWR(): Promise<void>;
}
import Client = require("../client/Client");
/**
* The leaderboard of a campaign
*/
declare class CampaignLeaderboard {
constructor(campaign: any, data: any);
/**
* The campaign
* @type {Campaign}
*/
campaign: Campaign;
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* Fetches the player
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
/**
* The player name
* @type {string}
*/
get playerName(): string;
/**
* The position
* @type {number}
*/
get position(): number;
/**
* The number of points
* @type {number}
*/
get points(): number;
}
import Club = require("./Club");
import TMMap = require("./TMMap");
/**
* The media images of an official campaign.
*/
declare class CampaignMedia {
constructor(client: any, data: any);
/**
* The client object of the campaign.
* @type {Client}
*/
client: Client;
/**
* The decal image URL of the campaign.
* @type {string}
*/
decal: string;
/**
* The button background image URL of the campaign.
* @type {string}
*/
buttonBackground: string;
/**
* The button foreground image URL of the campaign.
* @type {string}
*/
buttonForeground: string;
/**
* The live button background image URL of the campaign.
* @type {string}
*/
liveButtonBackground: string;
/**
* The live button foreground image URL of the campaign.
* @type {string}
*/
liveButtonForeground: string;
/**
* The popup background image URL of the campaign.
* @type {string}
*/
popupBackground: string;
/**
* The popup foreground image URL of the campaign.
* @type {string}
*/
popup: string;
}
/**
* The WR activity of a campaign
*/
declare class CampaignRecordActivity {
constructor(campaign: any, data: any);
/**
* The Campaign
* @type {Campaign}
*/
campaign: Campaign;
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The ID of the activity
* @type {number}
*/
get id(): number;
/**
* The leaderboard UID of the campaign
* @type {string}
*/
get leaderboardId(): string;
/**
* The map of the activity
* @returns {Promise<TMMap>}
*/
map(): Promise<TMMap>;
/**
* The map name
* @type {string}
*/
get mapName(): string;
/**
* The map author
* @returns {Promise<Player>}
*/
mapAuthor(): Promise<Player>;
/**
* The map author name
* @type {string}
*/
get mapAuthorName(): string;
/**
* The player who set the record
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
/**
* The player name who set the record
* @type {string}
*/
get playerName(): string;
/**
* The date of the record
* @type {Date}
*/
get date(): Date;
/**
* The time of the record
* @type {number}
*/
get time(): number;
/**
* The difference between the record and the previous one
* @type {number}
*/
get difference(): number;
}
import Player = require("./Player");

239
packages/bridge-shared/trackmania.io-f/typings/structures/Club.d.ts

@ -0,0 +1,239 @@
export = Club;
/**
* The Club class represents a club in Trackmania.
*/
declare class Club {
constructor(client: any, data: any);
/**
* The client object of the club
* @type {Client}
*/
client: Client;
/**
* The data of the club
* @type {Object}
* @private
*/
private _data;
/**
* The Club ID
* @type {number}
*/
get id(): number;
/**
* The Club name
* @type {string}
*/
get name(): string;
/**
* The Club tag
* @type {string}
*/
get tag(): string;
/**
* The Club description
* @type {string}
*/
get description(): string;
/**
* The Club logo URL
* @type {string}
*/
get logo(): string;
/**
* The Club decal URL
* @type {string}
*/
get decal(): string;
/**
* The Club background URL
* @type {string}
*/
get background(): string;
/**
* The club vertical background URL
* @type {string}
*/
get vertical(): string;
/**
* The club screens URL. Imares are in DDS format, except the sponsor 4x1 that may be in PNG.
* @type {Object<string, string>}
*/
get screens(): {
[x: string]: string;
};
/**
* The club creation date
* @type {Date}
*/
get createdAt(): Date;
/**
* The club popularity level
* @type {number}
*/
get popularity(): number;
/**
* The club state (public/private)
* @type {string}
*/
get state(): string;
/**
* Whether the club is featured
* @type {boolean}
*/
get featured(): boolean;
/**
* The club member count
* @type {number}
*/
get memberCount(): number;
/**
* The club creator player
* @returns {Promise<Player>}
* @example
* Client.clubs.get(54).then(async club => {
* const creator = await club.creator;
* console.log(creator.name);
* });
*/
creator(): Promise<Player>;
/**
* The club members (Members are sorted by role and club interaction time.)
* @param {number} [page=0] The page number
* @param {boolean} [cache=true] Whether to cache the result
* @returns {Promise<Array<ClubMember>>}
*/
fetchMembers(page?: number, cache?: boolean): Promise<Array<ClubMember>>;
/**
* The cache manager for members
* @type {CacheManager}
* @private
*/
private _membersCache;
/**
* The club activities
* @param {number} [page=0] The page number
* @param {boolean} [cache=true] Whether to cache the result
* @returns {Promise<Array<ClubActivity>>}
*/
fetchActivities(page?: number, cache?: boolean): Promise<Array<ClubActivity>>;
/**
* The cache manager for members
* @type {CacheManager}
* @private
*/
private _activitiesCache;
}
import Client = require("../client/Client");
import Player = require("./Player");
import Room = require("./Room");
/**
* Represents a club member (player) in the club
*/
declare class ClubMember {
constructor(club: any, data: any);
/**
* The club object
* @type {Club}
*/
club: Club;
/**
* The data of the club
* @type {Object}
* @private
*/
private _data;
/**
* The member
* @returns {Promise<Player>}
*/
member(): Promise<Player>;
/**
* The join date on the club
* @type {Date}
*/
get joinDate(): Date;
/**
* The member role
* @type {ClubMemberRole}
*/
get role(): string;
/**
* Whether the member is a club creator
* @type {boolean}
*/
get isCreator(): boolean;
/**
* Whether the member is a club admin
* @type {boolean}
*/
get isAdmin(): boolean;
/**
* Whether the member is a vip
* @type {boolean}
*/
get isVip(): boolean;
}
/**
* Represents a club activity in the club
*/
declare class ClubActivity {
constructor(club: any, data: any);
/**
* The club object
* @type {Club}
*/
club: Club;
/**
* The data of the club
* @type {Object}
* @private
*/
private _data;
/**
* The activity id
* @type {number}
*/
get id(): number;
/**
* The activity name
* @type {string}
*/
get name(): string;
/**
* The activity type
* @type {string}
*/
get type(): string;
/**
* Whether the activity is a public activity
* @type {boolean}
*/
get isPublic(): boolean;
/**
* The activity image URL
* @type {string}
*/
get media(): string;
/**
* Whether the activity is password protected
* @type {boolean}
*/
get isPasswordProtected(): boolean;
/**
* The activity external id
* @type {number}
*/
get externalId(): number;
/**
* If the activity is a campaign, returns the campaign object of the activity
* @returns {?Promise<Campaign>}
*/
campaign(): Promise<Campaign> | null;
/**
* If the activity is a room, returns the room object of the activity
* @returns {?Promise<Room>}
*/
room(): Promise<Room> | null;
}
import Campaign = require("./Campaign");

225
packages/bridge-shared/trackmania.io-f/typings/structures/Match.d.ts

@ -0,0 +1,225 @@
export = Match;
/**
* The match
*/
declare class Match {
constructor(client: any, data: any);
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The match ID
* @type {number}
*/
get id(): number;
/**
* The match live ID
* @type {string}
*/
get liveId(): string;
/**
* The match name
* @type {string}
*/
get name(): string;
/**
* The match type
* @type {?MatchmakingGroup}
*/
get type(): string;
/**
* The match group
* @type {string}
* @private
*/
private get group();
/**
* The match start date
* @type {Date}
*/
get startDate(): Date;
/**
* The match end date
* @type {Date}
*/
get endDate(): Date;
/**
* The match score direction
* @type {string}
* @private
*/
private get scoreDirection();
/**
* The match participant type
* @type {string}
* @private
*/
private get participantType();
/**
* The match script settings
* NOTE: Array is empty (api update?)
* @type {Array<MatchScriptSetting>}
* @private
*/
private get scriptSettings();
/**
* The match maps
* NOTE: Array is empty (api update?)
* @type {Array<MatchMap>}
* @private
*/
private get maps();
/**
* The match server id
* @type {number}
*/
get serverId(): number;
/**
* The match join link
* @type {string}
*/
get joinLink(): string;
/**
* The match status
* @type {MatchStatus}
*/
get status(): string;
/**
* The match players
* @type {Array<MatchPlayer>}
*/
get players(): MatchPlayer[];
/**
* The match teams (if match is completed)
* @type {?Array<MatchTeam>}
*/
get teams(): MatchTeam[];
}
import Client = require("../client/Client");
/**
* The player in the match
*/
declare class MatchPlayer {
constructor(match: any, data: any);
/**
* The match
* @type {Match}
*/
match: Match;
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The player name
* @type {string}
*/
get name(): string;
/**
* The player id
* @type {string}
*/
get id(): string;
/**
* The player rank in the match
* @type {number}
*/
get rank(): number;
/**
* The player score in the match
* @type {number}
*/
get score(): number;
/**
* The team index where the player is in the match
* @type {number}
*/
get teamIndex(): number;
/**
* The team where the player is (if the match is completed)
* @type {?MatchTeam}
*/
get team(): MatchTeam;
/**
* Whether the player is MVP (in a 3v3 match)
* @type {boolean}
*/
get isMVP(): boolean;
/**
* The matchmaking points of the player before the match
* @type {number}
*/
get mmPointsBefore(): number;
/**
* The matchmaking points of the player after the match
* @type {number}
*/
get mmPointsAfter(): number;
/**
* The matchmaking points of the player gained in the match
* @type {number}
*/
get mmPointsGained(): number;
/**
* The player object
* @type {Player}
*/
player(): Promise<import("./Player")>;
}
/**
* The team in the match
*/
declare class MatchTeam {
constructor(match: any, data: any);
/**
* The match
* @type {Match}
*/
match: Match;
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The team Index
* @type {number}
*/
get index(): number;
/**
* The team score
* @type {number}
*/
get score(): number;
/**
* The team name
* @type {?string | TeamName}
*/
get name(): string;
/**
* The team image (if the match is Royal)
* @type {?string}
*/
get image(): string;
}

64
packages/bridge-shared/trackmania.io-f/typings/structures/MatchmakingDivision.d.ts

@ -0,0 +1,64 @@
export = MatchmakingDivision;
/**
* Represents a division in the matchmaking system.
*/
declare class MatchmakingDivision {
constructor(client: any, typeId: any, division: any);
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The type of the division
* @type {number}
*/
typeId: number;
/**
* The division data
* @type {Object}
* @private
*/
private _division;
/**
* The division position
* @type {number}
*/
get position(): number;
/**
* The type name of the division
* @type {string}
*/
get typeName(): string;
/**
* The name of the division
* @type {string}
*/
get name(): string;
/**
* The rule identifier of this division
* @type {string}
*/
get rule(): string;
/**
* The minimum points to the division
* @type {number}
*/
get minPoints(): number;
/**
* The maximum points to the division
* @type {number}
*/
get maxPoints(): number;
/**
* The image of the division. If Royal, the crown
* @type {string}
*/
get image(): string;
/**
* The Royal Lion image of the division
* @type {string}
*/
get lion(): string;
}
import Client = require("../client/Client");

845
packages/bridge-shared/trackmania.io-f/typings/structures/Player.d.ts

@ -0,0 +1,845 @@
export = Player;
/**
* Represents a player in Trackmania.
*/
declare class Player {
constructor(client: any, data: any);
/**
* The client object of the player
* @type {Client}
*/
client: Client;
/**
* The data of the player
* @type {Object}
* @private
*/
private _data;
/**
* Constructs an array of the zone of the player
* @returns {Array<Object>}
* @private
*/
private _constructZoneArray;
/**
* The account ID of the player
* @type {string}
*/
get id(): string;
/**
* The login of the player
* @type {string}
*/
get login(): string;
/**
* The display name of the player
* @type {string}
*/
get name(): string;
/**
* The date of the player's first login
* @type {Date}
* @readonly
*/
get firstLogin(): Date;
/**
* The club tag of the player (non-formatted)
* @type {string}
*/
get clubTag(): string;
/**
* The last change of the player's club tag
* @type {Date}
* @readonly
*/
get lastClubTagChange(): Date;
/**
* The player's zone data with the ranking of the player in the zone
* @type {Array<PlayerZone>}
* @example
* // Generate a string of the player's zone data
* const string = player.zone.map(z=>z.name).join(', ');
*/
get zone(): PlayerZone[];
/**
* The player's trophy data
* @type {PlayerTrophies}
*/
get trophies(): PlayerTrophies;
/**
* The player's trophy data
* @type {PlayerTrophies}
* @private
*/
private _PlayerTrophies;
/**
* The player's meta data
* @type {PlayerMeta}
*/
get meta(): PlayerMeta;
/**
* The player's meta data
* @type {PlayerMeta}
* @private
*/
private _PlayerMeta;
/**
* The player's COTD Data
* @param {number} [page=0] The page number.
* @returns {Promise<PlayerCOTD>}
*/
cotd(page?: number): Promise<PlayerCOTD>;
/**
* The player's matchmaking data
* @param {MatchmakingGroup} [type="3v3"] The type of matchmaking data to return
* @returns {PlayerMatchmaking}
*/
matchmaking(type?: MatchmakingGroup): PlayerMatchmaking;
/**
* The player's matchmaking data
* @type {PlayerMatchmaking}
* @private
*/
private _PlayerMatchmaking;
}
import Client = require("../client/Client");
/**
* Represents a zone in a player
*/
declare class PlayerZone {
constructor(player: any, zone: any);
/**
* The player instance
* @type {Player}
*/
player: Player;
/**
* The client that instancied the Player
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The name of the zone
* @type {string}
*/
get name(): string;
/**
* The flag of the zone
* @type {string}
*/
get flag(): string;
/**
* The ranking of the player in the zone
* @type {number}
*/
get ranking(): number;
}
/**
* Represents the trophies of a player
*/
declare class PlayerTrophies {
constructor(player: any, data: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The points of the player
* @type {number}
*/
get points(): number;
/**
* The last time the player got a trophy
* @type {Date}
* @readonly
*/
get lastChange(): Date;
/**
* The echelon level of the player
* @type {PlayerEchelon}
*/
get echelon(): PlayerEchelon;
/**
* The player's echelon data
* @type {PlayerEchelon}
* @private
*/
private _PlayerEchelon;
/**
* The number of trophies the player has
* @param {number} [number=1] The trophy number, from 1 (bronze 1) to 9 (gold 3)
* @returns {number}
* @example
* // Get number of trophy 5 (aka silver 2 trophy)
* player.trophies.trophy(5);
*/
trophy(number?: number): number;
/**
* The number of trophies the player has
* @type {Array<number>}
*/
get trophies(): number[];
/**
* The last 25 trophies gains of the player
* @param {number} [page=0] The page number.
* @type {Array<PlayerTrophyHistory>}
*/
history(page?: number): Promise<PlayerTrophyHistory[]>;
}
/**
* Represents a player's metadata
*/
declare class PlayerMeta {
constructor(player: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The vanity name of the player, if the player has one, otherwise null
* @type {string}
*/
get vanity(): string;
/**
* The youtube link of the player, if the player has one, otherwise null
* @type {string}
*/
get youtube(): string;
/**
* The twitter link of the player, if the player has one, otherwise null
* @type {string}
*/
get twitter(): string;
/**
* The twitch channel link of the player, if the player has one, otherwise null
* @type {string}
*/
get twitch(): string;
/**
* The mastodon profile link of the player, if the player has one, otherwise null
* @type {string}
*/
get mastodon(): string;
/**
* The display URL of the player
* @type {string}
*/
get displayURL(): string;
/**
* Whether the player is in the TMGL group
* @type {boolean}
*/
get inTMGL(): boolean;
/**
* Whether the player is in the Nadeo company
* @type {boolean}
*/
get inNadeo(): boolean;
/**
* Whether the player is in the Openplanet & Trackmania.io team
* @type {boolean}
*/
get inTMIOTeam(): boolean;
/**
* Whether the player is a Openplanet & Trackmania.io sponsor
* @type {boolean}
*/
get isSponsor(): boolean;
/**
* If the player is a sponsor, this returns the sponsor's level
* @type {?number}
*/
get sponsorLevel(): number;
}
/**
* Represents a player's COTD object
*/
declare class PlayerCOTD {
constructor(player: any, data: any);
/**
* The Player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The number of COTDs played
* @type {number}
*/
get count(): number;
/**
* The Player COTD stats
* @type {PlayerCOTDStats}
*/
get stats(): PlayerCOTDStats;
/**
* The Player COTD stats
* @type {PlayerCOTDStats}
* @private
*/
private _stats;
/**
* Get the 25 recents COTD results
* @type {Array<PlayerCOTDResult>}
*/
get recentResults(): PlayerCOTDResult[];
}
import { MatchmakingGroup } from "../util/Constants";
/**
* Represents a player's stats in matchmaking
*/
declare class PlayerMatchmaking {
constructor(player: any, type: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The raw data of the player's matchmaking data based on the type
* @type {Object}
* @private
*/
private _data;
/**
* The type name of the matchmaking
* @type {string}
*/
get type(): string;
/**
* The type ID of the matchmaking
* @type {number}
*/
get typeId(): number;
/**
* The rank of the player on this matchmaking
* @type {number}
*/
get rank(): number;
/**
* The total number of players in this matchmaking
* @type {number}
*/
get totalPlayers(): number;
/**
* The MMR rank of the player on this matchmaking (score)
* @type {number}
*/
get score(): number;
/**
* The progression of the player on this matchmaking (can be number of wins for Royal, or score for 3v3)
* @type {number}
*/
get progression(): number;
/**
* The division of the player on this matchmaking
* @type {MatchmakingDivision}
*/
get division(): import("./MatchmakingDivision");
/**
* The division of the player on this matchmaking
* @type {MatchmakingDivision}
* @private
*/
private _MatchmakingDivision;
/**
* The history of recent matches on this matchmaking
* @param {number} [page=0] The page number to get
* @type {Promise<Array<PlayerMatchmakingMatchResult>>}
*/
history(page?: number): Promise<PlayerMatchmakingMatchResult[]>;
}
/**
* Represents a player's echelon
*/
declare class PlayerEchelon {
constructor(player: any, data: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object of the player
* @type {Client}
*/
client: Client;
/**
* The echelon number
* @type {number}
*/
number: number;
/**
* The name of the echelon
* @type {string}
*/
get name(): string;
/**
* The image URL of the echelon
* @type {string}
*/
get image(): string;
}
/**
* Represents the history of a player's trophies
*/
declare class PlayerTrophyHistory {
constructor(player: any, data: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The number of trophies the player has
* @param {number} [number=1] The trophy number, from 1 (bronze 1) to 9 (gold 3)
* @returns {number}
* @example
* // Get number of trophy 5 (aka silver 2 trophy) on the latest gain
* player.trophies.history[0].trophy(5);
*/
trophy(number?: number): number;
/**
* The number of trophies the player has
* @type {Array<number>}
*/
get trophies(): number[];
/**
* The date of the gain
* @type {Date}
*/
get date(): Date;
/**
* The rank of the player
* @type {number}
*/
get rank(): number;
/**
* The types of the achievement
* @type {PlayerTrophyAchievementType}
*/
get type(): PlayerTrophyAchievementType;
/**
* The achievement type object
* @type {PlayerTrophyAchievementType}
* @private
*/
private _achievement;
/**
* The map where the achievement was earned (if any)
* @returns {Promise<TMMap>|null}
*/
map(): Promise<TMMap> | null;
}
/**
* Represents a player's COTD stats
*/
declare class PlayerCOTDStats {
constructor(player: any, data: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The best stats in the primary COTD
* @type {PlayerCOTDStatsBest}
*/
get bestPrimary(): PlayerCOTDStatsBest;
/**
* The best stats in the primary COTD
* @type {PlayerCOTDStatsBest}
* @private
*/
private _bestprimary;
/**
* The best stats in all COTDs (including reruns)
* @type {PlayerCOTDStatsBest}
*/
get bestOverall(): PlayerCOTDStatsBest;
/**
* The best stats in all COTDs (including reruns)
* @type {PlayerCOTDStatsBest}
* @private
*/
private _bestoverall;
/**
* The total COTD wins in division 1
* @type {number}
*/
get totalWins(): number;
/**
* The total COTD wins in any divison
* @type {number}
*/
get totalDivWins(): number;
/**
* Average rank, float between 0 and 1
* @type {number}
*/
get averageRank(): number;
/**
* Average div rank (in any division), float between 0 and 1
* @type {number}
*/
get averageDivRank(): number;
/**
* Average division
* @type {number}
*/
get averageDiv(): number;
/**
* The win streak in division 1
* @type {number}
*/
get winStreak(): number;
/**
* The win streak in any division
* @type {number}
*/
get divWinStreak(): number;
}
/**
* Represents a player's COTD result
*/
declare class PlayerCOTDResult {
constructor(player: any, data: any);
/**
* The Player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The ID of the COTD
* @type {number}
*/
get id(): number;
/**
* The date of the COTD
* @type {Date}
* @readonly
*/
get date(): Date;
/**
* The name of the COTD
* @type {string}
*/
get name(): string;
/**
* The division of the COTD
* @type {number}
*/
get division(): number;
/**
* The overall rank on the COTD
* @type {number}
*/
get rank(): number;
/**
* The division rank on the COTD
* @type {number}
*/
get divisionRank(): number;
/**
* The score of the COTD
* @type {number}
*/
get score(): number;
/**
* The total number of players of the COTD
* @type {number}
*/
get totalPlayers(): number;
}
/**
* Represents a player's matchmaking match result
*/
declare class PlayerMatchmakingMatchResult {
constructor(player: any, data: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The player has win the match
* @type {boolean}
*/
get win(): boolean;
/**
* The player has leaved the match
* @type {boolean}
*/
get leave(): boolean;
/**
* The player is the most valuable player in the match
* @type {boolean}
*/
get mvp(): boolean;
/**
* The match LiveID
* @type {string}
*/
get liveId(): string;
/**
* The start date of the match
* @type {Date}
*/
get startDate(): Date;
/**
* The score of the player after this match
* @type {number}
*/
get afterScore(): number;
}
/**
* Represents the type of an achievement
*/
declare class PlayerTrophyAchievementType {
constructor(player: any, data: any);
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* Gets the type of the achievement
* @type {string}
*/
get type(): string;
/**
* Gets the ID of the achievement
* @type {string}
*/
get id(): string;
/**
* Gets the solo ranking achievement type (if the type is SoloRanking)
* @type {?string}
*/
get soloRankingType(): string;
/**
* Gets the solo ranking season ID (if the type is SoloRanking)
* @type {?string}
*/
get soloRankingSeasonId(): string;
/**
* Gets the competition id (if the type is CompetitionRanking)
* @type {?string}
*/
get competitionId(): string;
/**
* Gets the competition name (if the type is CompetitionRanking)
* @type {?string}
*/
get competitionName(): string;
/**
* Gets the competition stage (if the type is CompetitionRanking)
* @type {?string}
*/
get competitionStage(): string;
/**
* Gets the competition stage step (if the type is CompetitionRanking)
* @type {?string}
*/
get competitionStageStep(): string;
/**
* Gets the competition type (if the type is CompetitionRanking)
* @type {?string}
*/
get competitionType(): string;
/**
* Gets the Solo Medal type (if the type is SoloMedal)
* @type {?string}
*/
get soloMedalType(): string;
/**
* Gets the solo medal level (if the type is SoloMedal)
* @type {?number}
*/
get soloMedalLevel(): number;
/**
* Gets the server ID of the Live Match (if the type is LiveMatch)
* @type {?string}
*/
get liveMatchServerId(): string;
/**
* Gets the game mode of the Live Match (if the type is LiveMatch)
* @type {?string}
*/
get liveMatchGameMode(): string;
/**
* Gets the duration of the Live Match in seconds (if the type is LiveMatch)
* @type {?number}
*/
get liveMatchDuration(): number;
/**
* Gets the rank of the Live Match (if the type is LiveMatch)
* @type {?number}
*/
get liveMatchRank(): number;
/**
* Gets the trophy rank of the Live Match (if the type is LiveMatch)
* @type {?number}
*/
get liveMatchTrophyRank(): number;
}
import TMMap = require("../structures/TMMap");
/**
* Represents a player's COTD stats best stats
*/
declare class PlayerCOTDStatsBest {
constructor(PlayerCOTDStats: any, data: any);
/**
* The PlayerCOTDStats object
* @type {PlayerCOTDStats}
*/
stats: PlayerCOTDStats;
/**
* The player object
* @type {Player}
*/
player: Player;
/**
* The client object
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The best rank
* @type {number}
*/
get rank(): number;
/**
* The best rank date
* @type {Date}
* @readonly
*/
get rankDate(): Date;
/**
* The best div rank
* @type {number}
*/
get divRank(): number;
/**
* The best division
* @type {number}
*/
get division(): number;
/**
* The best divison date
* @type {Date}
* @readonly
*/
get divisionDate(): Date;
/**
* The best rank in a division
* @type {number}
*/
get rankInDivision(): number;
/**
* The best rank in a division date
* @type {Date}
* @readonly
*/
get rankInDivisionDate(): Date;
/**
* The division who got the best rank in a division
* @type {number}
*/
get divisionOfRankInDivision(): number;
}

99
packages/bridge-shared/trackmania.io-f/typings/structures/Room.d.ts

@ -0,0 +1,99 @@
export = Room;
/**
* Represents a room.
*/
declare class Room {
constructor(client: any, data: any);
/**
* The client that instantiated this room
* @type {Client}
*/
client: Client;
/**
* The data of the room
* @type {Object}
* @private
*/
private _data;
/**
* The ID of the room
* @type {number}
*/
get id(): number;
/**
* The club that this room belongs to
* @returns {Promise<Club>}
*/
club(): Promise<Club>;
/**
* The name of the room
* @type {string}
*/
get name(): string;
/**
* Whether this room is hosted on the cloud (Nadeo)
* @type {boolean}
*/
get isCloud(): boolean;
/**
* The login of the room (if it's not a cloud room)
* @type {?string}
*/
get login(): string;
/**
* The number of players in the room
* @type {number}
*/
get playerCount(): number;
/**
* The maximum number of players in the room
* @type {number}
*/
get maxPlayersCount(): number;
/**
* The region of the room (if it's on a cloud room)
* @type {RoomRegion}
*/
get region(): string;
/**
* The script name that currently runs in the room
* @type {string}
*/
get script(): string;
/**
* The script settings on the room.
* <info> Example of result:
*
* [{
*
* key: 'S_DecoImageUrl_Checkpoint',
*
* value: 'https://trackmania-prod-nls-file-store-s3.cdn.ubi.com/club/decal/5f62400600952.png?updateTimestamp=1600274438.png'
*
* }]
*
* </info>
* @type {Array<Object<string, string|number|boolean>>} x must be "key" and "value"
*/
get scriptSettings(): {
[x: string]: string | number | boolean;
}[];
/**
* The image URL of the room
* @type {string}
*/
get imageUrl(): string;
/**
* The maps on the room
* @returns {Promise<Array<TMMap>>}
* @example
* Client.rooms.get(228, 82160).then(async room => {
* const maps = await room.maps();
* maps.forEach(map => console.log(map.name));
* });
*/
maps(): Promise<Array<TMMap>>;
}
import Client = require("../client/Client");
import Club = require("./Club");
import TMMap = require("./TMMap");

49
packages/bridge-shared/trackmania.io-f/typings/structures/Splashscreen.d.ts

@ -0,0 +1,49 @@
export = Splashscreen;
/**
* Represents a news item.
*/
declare class Splashscreen {
constructor(client: any, data: any);
/**
* The client object of the news page
* @type {Client}
*/
client: Client;
/**
* The data of the news page
* @type {Object}
* @private
*/
private _data;
/**
* The news item's id
* @type {number}
*/
get id(): number;
/**
* The news item's headline
* @type {string}
*/
get headline(): string;
/**
* The news item's text content
* @type {string}
*/
get body(): string;
/**
* The link to the news item on trackmania.com
* @type {string}
*/
get link(): string;
/**
* The news item's accompanying media file as a link
* @type {string}
*/
get media(): string;
/**
* The news item's time of publishing
* @type {Date}
*/
get time(): Date;
}
import Client = require("../client/Client");

397
packages/bridge-shared/trackmania.io-f/typings/structures/TMEvent.d.ts

@ -0,0 +1,397 @@
export = TMEvent;
/**
* Represents a Event in Trackmania.
*/
declare class TMEvent {
constructor(client: any, data: any);
/** The client instance
* @type {Client}
*/
client: Client;
/**
* The event's data.
* @type {Object}
* @private
*/
private _data;
/**
* The event's ID.
* @type {number}
*/
get id(): number;
/**
* The number of players in the event.
* @type {number}
*/
get size(): number;
/**
* The event's Live ID.
* @type {string}
*/
get liveId(): string;
/**
* The creator of the event.
* @returns {Promise<Player>}
*/
creator(): Promise<Player>;
/**
* The event's name.
* @type {string}
*/
get name(): string;
/**
* The event's description.
* @type {string}
*/
get description(): string;
/**
* The event's registration start date.
* @type {Date}
*/
get registrationStart(): Date;
/**
* The event's registration end date.
* @type {Date}
*/
get registrationEnd(): Date;
/**
* The event's start date.
* @type {Date}
*/
get start(): Date;
/**
* The event's end date.
* @type {Date}
*/
get end(): Date;
/**
* The event's leaderboard id.
* @type {number}
*/
get leaderboardId(): number;
/**
* The event's manialink (if any).
* @type {?string}
*/
get manialink(): string;
/**
* The event's rules URL (if any).
* @type {?string}
*/
get rulesUrl(): string;
/**
* The event's stream URL (if any).
* @type {?string}
*/
get stream(): string;
/**
* The event's website (if any).
* @type {?string}
*/
get website(): string;
/**
* The event's logo URL.
* @type {string}
*/
get logo(): string;
/**
* The event's vertical banner URL.
* @type {string}
*/
get vertical(): string;
/**
* The event's rounds.
* @type {Array<TMEventRound>}
*/
get rounds(): TMEventRound[];
}
import Client = require("../client/Client");
import Player = require("./Player");
/**
* Represents a round in a TMEvent.
*/
declare class TMEventRound {
constructor(event: any, data: any);
/**
* The event instance
* @type {TMEvent}
*/
event: TMEvent;
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The round's data.
* @type {Object}
* @private
*/
private _data;
/**
* The challenge's CacheManager instance
* @type {CacheManager}
* @private
*/
private _challengesCache;
/**
* The round's ID.
* @type {number}
*/
get id(): number;
/**
* The round's name.
* @type {string}
*/
get name(): string;
/**
* The round's status.
* @type {string}
*/
get status(): string;
/**
* The round's matches.
* @type {Array<TMEventRoundMatch>}
*/
get matches(): TMEventRoundMatch[];
/**
* The round's challenges.
* @param {boolean} [cache=this.client.options.cache.enabled] Wether to get the challenges from the cache or not.
* @returns {Promise<Array<TMEventChallenge>>}
*/
challenges(cache?: boolean): Promise<Array<TMEventChallenge>>;
/**
* Fetches the round's challenges.
* @param {number} index The index of the challenge to fetch.
* @param {boolean} [cache=this.client.options.cache.enabled] Wether to cache the challenges or not.
* @returns {Promise<Array<TMEventChallenge>>}
* @private
*/
private _fetchChallenge;
}
/**
* Represents a match in a TMEventRound.
*/
declare class TMEventRoundMatch {
constructor(round: any, data: any);
/**
* The round instance
* @type {TMEventRound}
*/
round: TMEventRound;
/**
* The event instance
* @type {TMEvent}
*/
event: TMEvent;
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The match's data.
* @type {Object}
* @private
*/
private _data;
/**
* The match's results CacheManager instance
* @type {CacheManager}
* @private
*/
private _resultsCache;
/**
* The match's ID.
* @type {number}
*/
get id(): number;
/**
* The match's name.
* @type {string}
*/
get name(): string;
/**
* Whether the match is completed.
* @type {boolean}
*/
get isCompleted(): boolean;
/**
* The match's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the results.
* @returns {Promise<Array<TMEventRoundMatchResult>>}
*/
getResults(page?: number, cache?: boolean): Promise<Array<TMEventRoundMatchResult>>;
/**
* Fetches the match's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the results.
* @returns {Promise<Array<TMEventRoundMatchResult>>}
* @private
*/
private _fetchResults;
}
/**
* Represents a challenge in a TMEventRound.
*/
declare class TMEventChallenge {
constructor(round: any, data: any);
/**
* The round instance
* @type {TMEventRound}
*/
round: TMEventRound;
/**
* The event instance
* @type {TMEvent}
*/
event: TMEvent;
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The challenge's data.
* @type {Object}
* @private
*/
private _data;
/**
* The challenge's results CacheManager instance
* @type {CacheManager}
* @private
*/
private _resultsCache;
/**
* The challenge's ID.
* @type {number}
*/
get id(): number;
/**
* The challenge's name.
* @type {string}
*/
get name(): string;
/**
* The challenge's status.
* @type {string}
*/
get status(): string;
/**
* The challenge's rooms number.
* @type {number}
*/
get rooms(): number;
/**
* The challenge's maps.
* @returns {Promise<Array<TMMap>>}
*/
getMaps(): Promise<Array<TMMap>>;
/**
* The challenge's admins.
* @returns {Promise<Array<Player>>}
*/
getAdmins(): Promise<Array<Player>>;
/**
* The challenge's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to get the results from cache.
* @returns {Promise<Array<TMEventChallengeResult>>}
*/
getResults(page?: number, cache?: boolean): Promise<Array<TMEventChallengeResult>>;
/**
* Fetches the match's results.
* @param {number} [page=0] The page number.
* @param {boolean} [cache=this.client.options.cache.enabled] Whether to cache the results.
* @returns {Promise<Array<TMEventChallengeResult>>}
* @private
*/
private _fetchResults;
}
/**
* Represents a result in a TMEventRoundMatch.
*/
declare class TMEventRoundMatchResult {
constructor(match: any, data: any);
/**
* The match instance
* @type {TMEventRoundMatch}
*/
match: TMEventRoundMatch;
/**
* The event instance
* @type {TMEvent}
*/
event: TMEvent;
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The result's data.
* @type {Object}
* @private
*/
private _data;
/**
* The player that got the result.
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
/**
* The position of the player.
* @type {number}
*/
get position(): number;
/**
* The score of the player.
* @type {number}
*/
get score(): number;
}
import TMMap = require("./TMMap");
/**
* Represents a result in a TMEventChallenge.
*/
declare class TMEventChallengeResult {
constructor(challenge: any, data: any);
/**
* The challenge instance
* @type {TMEventChallenge}
*/
challenge: TMEventChallenge;
/**
* The event instance
* @type {TMEvent}
*/
event: TMEvent;
/**
* The client instance
* @type {Client}
*/
client: Client;
/**
* The result's data.
* @type {Object}
* @private
*/
private _data;
/**
* The player.
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
/**
* The position of the player.
* @type {number}
*/
get position(): number;
/**
* The score of the player.
* @type {number}
*/
get score(): number;
}

300
packages/bridge-shared/trackmania.io-f/typings/structures/TMMap.d.ts

@ -0,0 +1,300 @@
export = TMMap;
/**
* Represents a map on Trackmania.
*/
declare class TMMap {
constructor(client: any, data: any);
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The map data.
* @type {Object}
* @private
*/
private _data;
/**
* The map medal times.
* @type {TMMapMedalTimes}
*/
medalTimes: TMMapMedalTimes;
/**
* The map cached leaderboard data. You should use the leaderboardLoadMore() the first time to load the leaderboard.
* @type {Array<TMMapLeaderboard>}
*/
leaderboard: Array<TMMapLeaderboard>;
/**
* The map name.
* @type {string}
*/
get name(): string;
/**
* The map id.
* @type {string}
*/
get id(): string;
/**
* The map unique id.
* @type {string}
*/
get uid(): string;
/**
* The map Storage Object ID.
* @type {string}
*/
get storageId(): string;
/**
* The map author's name.
* @type {string}
*/
get authorName(): string;
/**
* The map author.
* @returns {Promise<Player>}
* @example
* Client.maps.get('z28QXoFnpODEGgg8MOederEVl3j').then(async map => {
* const author = await map.author();
* console.log(`The map author is ${author.name}`);
* });
*/
author(): Promise<Player>;
/**
* The map submitter's name.
* @type {string}
*/
get submitterName(): string;
/**
* The map submitter.
* @returns {Promise<Player>}
*/
submitter(): Promise<Player>;
/**
* The environment for this map.
* @type {string}
*/
get environment(): string;
/**
* The map file name.
* @type {string}
*/
get fileName(): string;
/**
* The map uploaded date.
* @type {Date}
*/
get uploaded(): Date;
/**
* The map URL.
* @type {string}
*/
get url(): string;
/**
* The map thumbnail (from Nadeo services, direct download).
* @type {string}
*/
get thumbnail(): string;
/**
* The map thumbnail (cached from trackmania.io, can show).
* @type {string}
*/
get thumbnailCached(): string;
/**
* The map exchange id, if the map is on trackmania.exchange, else null.
* @type {?string}
*/
get exchangeId(): string;
/**
* The map informations on trackmania.exchange.
* @returns {Promise<?TMExchangeMap>}
*/
exchange(): Promise<TMExchangeMap | null>;
/**
* Load more results in the leaderboard.
* @param {number} [nbOfResults=100] The number of results to load. (max 100)
* @returns {Promise<?Array<TMMapLeaderboard>>}
*/
leaderboardLoadMore(nbOfResults?: number): Promise<Array<TMMapLeaderboard> | null>;
/**
* Get a leaderboard in a specific position. Must be between 1 and 10000.
* @param {number} position The position of the leaderboard.
* @returns {Promise<?TMMapLeaderboard>}
*/
leaderboardGet(position: number): Promise<TMMapLeaderboard | null>;
/**
* Subscribe to the map WR updates.
* <info>When a new WR is set, the event {@link TMMap#e-wr} will be fired</info>
* @returns {Promise<void>}
* @example
* Client.maps.get('z28QXoFnpODEGgg8MOederEVl3j').then(map => {
* map.subWR();
* map.on('wr', (old, new) => {
* console.log(`New WR for ${map.name} is ${new.playerName} (${new.time})`);
* });
* });
*/
subWR(): Promise<void>;
}
import Client = require("../client/Client");
/**
* Represents the medals times on a map.
*/
declare class TMMapMedalTimes {
constructor(map: any);
/**
* The map object.
* @type {TMMap}
*/
map: TMMap;
/**
* The map author time.
* @type {number}
*/
author: number;
/**
* The map gold time.
* @type {number}
*/
gold: number;
/**
* The map silver time.
* @type {number}
*/
silver: number;
/**
* The map bronze time.
* @type {number}
*/
bronze: number;
}
/**
* Represents the map leaderboard.
*/
declare class TMMapLeaderboard {
constructor(map: any, data: any);
/**
* The map Instance
* @type {TMMap}
*/
map: TMMap;
/**
* The Client instance
* @type {Client}
*/
client: Client;
/**
* The data
* @type {Object}
* @private
*/
private _data;
/**
* The player that got this leaderboard
* @returns {Promise<Player>}
*/
player(): Promise<Player>;
/**
* The player name on this leaderboard
* @type {string}
*/
get playerName(): string;
/**
* The player club tag on this leaderboard
* @type {string}
*/
get playerClubTag(): string;
/**
* The position of the player on this leaderboard
* @type {number}
*/
get position(): number;
/**
* The time in milliseconds of the player
* @type {number}
*/
get time(): number;
/**
* The date when the player get this leaderboard
* @type {Date}
*/
get date(): Date;
/**
* The ghost URL
* @type {string}
*/
get ghost(): string;
}
import Player = require("./Player");
/**
* Represents the map details from Trackmania.exchange.
*/
declare class TMExchangeMap {
constructor(map: any, data: any);
/**
* The map instance.
* @type {TMMap}
*/
map: TMMap;
/**
* The client instance.
* @type {Client}
*/
client: Client;
/**
* The map data.
* @type {Object}
* @private
*/
private _data;
/**
* The map exchange id.
* @type {number}
*/
get id(): number;
/**
* The map name.
* @type {string}
*/
get name(): string;
/**
* The map author.
* @type {string}
*/
get author(): string;
/**
* The map description.
* @type {string}
*/
get description(): string;
/**
* The map length.
* @type {string}
*/
get length(): string;
/**
* The map difficulty.
* @type {string}
*/
get difficulty(): string;
/**
* The map upload date.
* @type {Date}
*/
get uploaded(): Date;
/**
* The map last update date.
* @type {Date}
*/
get updated(): Date;
/**
* The map award count.
* @type {number}
*/
get awards(): number;
/**
* The map download link.
* @type {string}
*/
get download(): string;
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save