nsarchive 3.0.0a7__tar.gz → 3.0.0a8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {nsarchive-3.0.0a7 → nsarchive-3.0.0a8}/PKG-INFO +3 -2
  2. nsarchive-3.0.0a8/nsarchive/__init__.py +30 -0
  3. {nsarchive-3.0.0a7/nsarchive/instances → nsarchive-3.0.0a8/nsarchive/interfaces}/_economy.py +15 -30
  4. nsarchive-3.0.0a8/nsarchive/interfaces/_entities.py +206 -0
  5. nsarchive-3.0.0a8/nsarchive/interfaces/_justice.py +50 -0
  6. nsarchive-3.0.0a8/nsarchive/interfaces/_state.py +142 -0
  7. nsarchive-3.0.0a8/nsarchive/mandate.py +50 -0
  8. {nsarchive-3.0.0a7/nsarchive/cls → nsarchive-3.0.0a8/nsarchive/models}/base.py +23 -5
  9. {nsarchive-3.0.0a7/nsarchive/cls → nsarchive-3.0.0a8/nsarchive/models}/economy.py +34 -13
  10. {nsarchive-3.0.0a7/nsarchive/cls → nsarchive-3.0.0a8/nsarchive/models}/entities.py +139 -72
  11. nsarchive-3.0.0a8/nsarchive/models/justice.py +108 -0
  12. nsarchive-3.0.0a8/nsarchive/models/republic.py +125 -0
  13. nsarchive-3.0.0a8/nsarchive/models/scale.py +23 -0
  14. nsarchive-3.0.0a8/nsarchive/models/state.py +59 -0
  15. {nsarchive-3.0.0a7 → nsarchive-3.0.0a8}/pyproject.toml +1 -1
  16. nsarchive-3.0.0a7/nsarchive/__init__.py +0 -25
  17. nsarchive-3.0.0a7/nsarchive/cls/archives.py +0 -93
  18. nsarchive-3.0.0a7/nsarchive/cls/republic.py +0 -149
  19. nsarchive-3.0.0a7/nsarchive/instances/_entities.py +0 -281
  20. nsarchive-3.0.0a7/nsarchive/instances/_republic.py +0 -339
  21. {nsarchive-3.0.0a7 → nsarchive-3.0.0a8}/LICENSE +0 -0
  22. {nsarchive-3.0.0a7 → nsarchive-3.0.0a8}/README.md +0 -0
  23. {nsarchive-3.0.0a7 → nsarchive-3.0.0a8}/nsarchive/assets/default_avatar.png +0 -0
  24. {nsarchive-3.0.0a7 → nsarchive-3.0.0a8}/nsarchive/utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: nsarchive
3
- Version: 3.0.0a7
3
+ Version: 3.0.0a8
4
4
  Summary: API-wrapper pour récupérer des données liées à Nation
5
5
  License: GPL-3.0
6
6
  Author: happex
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
14
15
  Requires-Dist: pillow (>=10.4,<11.0)
15
16
  Requires-Dist: requests (>=2.31,<3.0)
16
17
  Description-Content-Type: text/markdown
@@ -0,0 +1,30 @@
1
+ """
2
+ nsarchive - API-wrapper pour récupérer des données liées à Nation.
3
+
4
+ Version: 3.0.0-alpha.8
5
+ Date de sortie: 2025-08-04
6
+ License: GPL-3.0
7
+ Auteur : happex <110610727+okayhappex@users.noreply.github.com>
8
+
9
+ Dependencies:
10
+ - Python ^3.10
11
+ - pillow ^10.4
12
+
13
+ Le fichier README.md fournit des détails supplémentaires pour l'utilisation.
14
+ """
15
+
16
+ # Import des types
17
+ from .models.base import NSID
18
+ from .models.entities import *
19
+ from .models.economy import *
20
+
21
+ from .models.republic import *
22
+ from .models.state import *
23
+ from .models.justice import *
24
+
25
+ # Import des interfaces
26
+ from .models.base import Interface
27
+ from .interfaces._entities import EntityInterface
28
+ from .interfaces._economy import EconomyInterface
29
+ from .interfaces._state import StateInterface
30
+ from .interfaces._justice import JusticeInterface
@@ -1,13 +1,12 @@
1
1
  import time
2
2
 
3
- from ..cls.base import *
4
- from ..cls.archives import *
5
- from ..cls.economy import *
3
+ from ..models.base import *
4
+ from ..models.economy import *
6
5
 
7
- from ..cls import economy # Pour les default_headers
6
+ from ..models import economy # Pour les default_headers
8
7
 
9
- class EconomyInstance(Instance):
10
- """Indisponible dans cette version."""
8
+ class EconomyInterface(Interface):
9
+ """Interface qui vous permettra d'interagir avec les comptes en banque et les transactions économiques."""
11
10
 
12
11
  def __init__(self, url: str, token: str) -> None:
13
12
  super().__init__(url, token)
@@ -43,9 +42,7 @@ class EconomyInstance(Instance):
43
42
  return None
44
43
 
45
44
  account = BankAccount(id)
46
- account._url = f"{self.url}/bank/accounts/{account.id}"
47
-
48
- account._load(_data)
45
+ account._load(_data, self.url, self.default_headers)
49
46
 
50
47
  return account
51
48
 
@@ -107,9 +104,7 @@ class EconomyInstance(Instance):
107
104
  account = BankAccount(_acc["owner_id"])
108
105
 
109
106
  account.id = NSID(_acc['id'])
110
- account._url = f"{self.url}/bank/accounts/{account.id}"
111
-
112
- account._load(_acc)
107
+ account._load(_acc, self.url, self.default_headers)
113
108
 
114
109
  res.append(account)
115
110
 
@@ -144,9 +139,7 @@ class EconomyInstance(Instance):
144
139
  return None
145
140
 
146
141
  inventory = Inventory(id)
147
- inventory._url = f"{self.url}/bank/inventories/{inventory.id}"
148
-
149
- inventory._load(_data)
142
+ inventory._load(_data, self.url, self.default_headers)
150
143
 
151
144
  return inventory
152
145
 
@@ -201,9 +194,7 @@ class EconomyInstance(Instance):
201
194
  inventory = Inventory(_inv["owner_id"])
202
195
 
203
196
  inventory.id = NSID(_inv['id'])
204
- inventory._url = f"{self.url}/bank/inventories/{inventory.id}"
205
-
206
- inventory._load(_inv)
197
+ inventory._load(_inv, self.url, self.default_headers)
207
198
 
208
199
  res.append(inventory)
209
200
 
@@ -238,10 +229,9 @@ class EconomyInstance(Instance):
238
229
  return None
239
230
 
240
231
  item = Item()
241
- item.id = id
242
- item._url = f"{self.url}/marketplace/items/{item.id}"
243
232
 
244
- item._load(_data)
233
+ item.id = id
234
+ item._load(_data, self.url, self.default_headers)
245
235
 
246
236
  return item
247
237
 
@@ -294,9 +284,7 @@ class EconomyInstance(Instance):
294
284
  item = Item()
295
285
 
296
286
  item.id = NSID(_item['id'])
297
- item._url = f"{self.url}/marketplace/items/{item.id}"
298
-
299
- item._load(_item)
287
+ item._load(_item, self.url, self.default_headers)
300
288
 
301
289
  res.append(item)
302
290
 
@@ -332,10 +320,9 @@ class EconomyInstance(Instance):
332
320
  return None
333
321
 
334
322
  sale = Sale()
335
- sale.id = id
336
- sale._url = f"{self.url}/marketplace/sales/{sale.id}"
337
323
 
338
- sale._load(_data)
324
+ sale.id = id
325
+ sale._load(_data, self.url, self.default_headers)
339
326
 
340
327
  return sale
341
328
 
@@ -369,9 +356,7 @@ class EconomyInstance(Instance):
369
356
  sale = Sale()
370
357
 
371
358
  sale.id = NSID(_sale['id'])
372
- sale._url = f"{self.url}/marketplace/sales/{sale.id}"
373
-
374
- sale._load(_sale)
359
+ sale._load(_sale, self.url, self.default_headers)
375
360
 
376
361
  res.append(sale)
377
362
 
@@ -0,0 +1,206 @@
1
+ from ..models.base import *
2
+ from ..models.entities import *
3
+
4
+ from ..models import entities # Pour les default_headers
5
+
6
+ class EntityInterface(Interface):
7
+ """
8
+ Interface qui vous permettra d'interagir avec les profils des membres ainsi que les différents métiers et secteurs d'activité.
9
+
10
+ ## Informations disponibles
11
+ - Profil des membres et des entreprises: `.User | .Organization | .Entity`
12
+ - Participation d'un membre à différent votes: `.User | .Organization | .Entity`
13
+ - Appartenance et permissions d'un membre dans un groupe: `.GroupMember.MemberPermissions`
14
+ - Position légale et permissions d'une entité: `.Position.Permissions`
15
+ - Sanctions et modifications d'une entité: `.Action[ .AdminAction | .Sanction ]`
16
+ """
17
+
18
+ def __init__(self, url: str, token: str = None) -> None:
19
+ super().__init__(url, token)
20
+
21
+ """
22
+ ---- ENTITÉS ----
23
+ """
24
+
25
+ def get_entity(self, id: NSID, _class: str = None) -> User | Organization | Entity:
26
+ """
27
+ Fonction permettant de récupérer le profil public d'une entité.\n
28
+
29
+ ## Paramètres
30
+ id: `NSID`
31
+ ID héxadécimal de l'entité à récupérer
32
+ _class: `str`
33
+ Classe du modèle à prendre (`.User` ou `.Organization`)
34
+
35
+ ## Renvoie
36
+ - `.User` dans le cas où l'entité choisie est un membre
37
+ - `.Organization` dans le cas où c'est un groupe
38
+ - `.Entity` dans le cas où c'est indéterminé
39
+ """
40
+
41
+ id = NSID(id)
42
+
43
+ if _class == "user":
44
+ _data = self._get_by_ID('individuals', id)
45
+ elif _class == "group":
46
+ _data = self._get_by_ID('organizations', id)
47
+ else:
48
+ _data = self._get_by_ID('entities', id)
49
+
50
+ if _data is None: # ID inexistant chez les entités
51
+ return None
52
+
53
+ if _data['_class'] == 'individuals':
54
+ entity = User(id)
55
+ elif _data['_class'] == 'organizations':
56
+ entity = Organization(id)
57
+ else:
58
+ entity = Entity(id)
59
+
60
+ entity._load(_data, self.url, self.default_headers)
61
+
62
+ return entity
63
+
64
+ def create_entity(self, id: NSID, name: str, _class: str, position: str = 'membre', zone: int = 10):
65
+ """
66
+ Fonction permettant de créer ou modifier une entité.
67
+
68
+ ## Paramètres
69
+ - id (`NSID`): Identifiant NSID
70
+ - name (`str`): Nom d'usage
71
+ - _class (`"user"` ou `"group"`): Type de l'entité
72
+ - position (`str`, optionnel): ID de la position civile
73
+ - zone (`int`, optionnel): ID de la zone civile
74
+ """
75
+
76
+ id = NSID(id)
77
+
78
+ if _class in ('group', 'organization'):
79
+ _class = 'organizations'
80
+ elif _class in ('user', 'individual'):
81
+ _class = 'individuals'
82
+ else:
83
+ return
84
+
85
+ self._put_in_db(
86
+ f"/new_model/{_class}?id={urllib.parse.quote(id)}&name={urllib.parse.quote(name)}&position={urllib.parse.quote(position)}&zone={urllib.parse.quote(zone)}",
87
+ headers = self.default_headers,
88
+ use_PUT = True
89
+ )
90
+
91
+ entity = self.get_entity(id)
92
+
93
+ if _class == "individuals":
94
+ entity._url = f"{self.url}/model/individuals/{id}"
95
+ elif isinstance(entity, Organization):
96
+ entity._url = f"{self.url}/model/organizations/{id}"
97
+ entity.avatar_url = f"{entity._url}/avatar"
98
+ else:
99
+ entity._url = f"{self.url}/model/entities/{id}"
100
+
101
+ return entity
102
+
103
+
104
+ def delete_entity(self, entity: Entity):
105
+ """
106
+ Fonction permettant de supprimer le profil d'une entité
107
+
108
+ ## Paramètres
109
+ entity: `.Entity`\n
110
+ L'entité à supprimer
111
+ """
112
+
113
+ res = requests.post(f"{entity._url}/delete", headers = self.default_headers,)
114
+
115
+ if res.status_code != 200:
116
+ res.raise_for_status()
117
+
118
+ def fetch_entities(self, **query: typing.Any) -> list[ Entity | User | Organization ]:
119
+ """
120
+ Récupère une liste d'entités en fonction d'une requête.
121
+
122
+ ## Paramètres
123
+ query: `**dict`\n
124
+ La requête pour filtrer les entités.
125
+
126
+ ## Renvoie
127
+ - `list[.Entity | .User | .Organization]`
128
+ """
129
+
130
+ if "_class" in query.keys():
131
+ if query["_class"] == "individuals":
132
+ del query["_class"]
133
+ _res = self.fetch('individuals', **query)
134
+ elif query["_class"] == "organizations":
135
+ del query["_class"]
136
+ _res = self.fetch('organizations', **query)
137
+ else:
138
+ del query["_class"]
139
+ _res = self.fetch('entities', **query)
140
+ else:
141
+ _res = self.fetch('entities', **query)
142
+
143
+ res = []
144
+
145
+ for _entity in _res:
146
+ if _entity is None: continue
147
+
148
+ if _entity['_class'] == 'individuals':
149
+ entity = User(_entity["id"])
150
+ elif _entity['_class'] == 'organizations':
151
+ entity = Organization(_entity["id"])
152
+ else:
153
+ entity = Entity(_entity["id"])
154
+
155
+ entity._load(_entity, self.url, self.default_headers)
156
+
157
+ res.append(entity)
158
+
159
+ return res
160
+
161
+
162
+
163
+ def get_position(self, id: str) -> Position:
164
+ """
165
+ Récupère une position légale (métier, domaine professionnel).
166
+
167
+ ## Paramètres
168
+ id: `str`\n
169
+ ID de la position (SENSIBLE À LA CASSE !)
170
+
171
+ ## Renvoie
172
+ - `.Position`
173
+ """
174
+
175
+ _data = self._get_by_ID('positions', id)
176
+
177
+ if _data is None:
178
+ return None
179
+
180
+ position = Position(id)
181
+ position._load(_data, self.url, self.default_headers)
182
+
183
+ return position
184
+
185
+ def fetch_positions(self, **query: typing.Any) -> list[Position]:
186
+ """
187
+ Récupère une liste de positions en fonction d'une requête.
188
+
189
+ ## Paramètres
190
+ query: `**dict`\n
191
+ La requête pour filtrer les positions.
192
+
193
+ ## Renvoie
194
+ - `list[.Position]`
195
+ """
196
+
197
+ _res = self.fetch('positions', **query)
198
+ res = []
199
+
200
+ for _data in _res:
201
+ pos = Position()
202
+ pos._load(_data, self.url, self.default_headers)
203
+
204
+ res.append(pos)
205
+
206
+ return res
@@ -0,0 +1,50 @@
1
+ import requests
2
+ import time
3
+
4
+ from ..models.base import *
5
+ from ..models.justice import *
6
+
7
+ class JusticeInterface(Interface):
8
+ """
9
+ Gère les procès, sanctions et signalements.
10
+ """
11
+
12
+ def __init__(self, url: str, token: str) -> None:
13
+ super().__init__(url, token)
14
+
15
+ """
16
+ SIGNALEMENTS
17
+ """
18
+
19
+ def get_report(self, id: NSID) -> Report:
20
+ res = requests.get(
21
+ f"{self.url}/justice/reports/{id}",
22
+ headers = self.default_headers,
23
+ )
24
+
25
+ if res.status_code != 200:
26
+ res.raise_for_status()
27
+
28
+ report = Report(id)
29
+ report._load(res.json(), f"{self.url}/justice/reports/{id}", self.default_headers)
30
+
31
+ return report
32
+
33
+ def submit_report(self, target: NSID, reason: str = None, details: str = None) -> Report:
34
+ payload = {}
35
+ if reason: payload['reason'] = reason
36
+ if details: payload['details'] = details
37
+
38
+ res = requests.put(
39
+ f"{self.url}/justice/submit_report?target={target}",
40
+ headers = self.default_headers,
41
+ json = payload
42
+ )
43
+
44
+ if res.status_code != 200:
45
+ res.raise_for_status()
46
+
47
+ report = Report(NSID(res.json()['id']))
48
+ report._load(res.json(), f"{self.url}/justice/reports/{report.id}", self.default_headers)
49
+
50
+ return report
@@ -0,0 +1,142 @@
1
+ import time
2
+
3
+ from ..models.base import *
4
+ from ..models.republic import *
5
+ from ..models.state import *
6
+ from ..models.scale import *
7
+
8
+ class StateInterface(Interface):
9
+ """
10
+ Gère les interactions avec les votes et les officiers.
11
+
12
+ ## Informations
13
+ - Liste des partis enregistrés: `.Party`
14
+ - Liste des élections: `.Election`
15
+ - Liste des officiers et candidats: `.Officer | .Candidate`
16
+ - Résultats des votes: `.Vote`
17
+ """
18
+
19
+ def __init__(self, url: str, token: str) -> None:
20
+ super().__init__(url, token)
21
+
22
+ """
23
+ ---- VOTES ----
24
+ """
25
+
26
+ def get_vote(self, id: NSID) -> Vote:
27
+ """
28
+ Récupère un vote.
29
+
30
+ ## Paramètres
31
+ id: `NSID`\n
32
+ ID du vote.
33
+
34
+ ## Renvoie
35
+ - `.Vote`
36
+ """
37
+
38
+ id = NSID(id)
39
+ res = requests.get(f"{self.url}/votes/{id}", headers = self.default_headers)
40
+
41
+ if not res:
42
+ return None
43
+
44
+ _data = res.json()
45
+
46
+ vote = Vote(id, _data['title'])
47
+ vote._load(_data, url = f"{self.url}/votes/{id}", headers = self.default_headers)
48
+
49
+ return vote
50
+
51
+ def open_vote(self, title: str = None, options: list[dict] = [], end: int = 0) -> Vote:
52
+ """
53
+ Déclenche un vote dans la base de données.
54
+
55
+ ## Paramètres
56
+ - title: `str`\n
57
+ Titre du vote
58
+ - options: list[dict]\n
59
+ Liste des choix disponibles
60
+ - end: `int`\n
61
+ Fin du vote (timestamp)
62
+ """
63
+
64
+ payload = {
65
+ "options": options,
66
+ "end_date": end
67
+ }
68
+
69
+ if title:
70
+ payload['title'] = title
71
+
72
+ res = requests.put(f"{self.url}/open_vote", headers = self.default_headers, json = _data)
73
+
74
+ if res.status_code == 200:
75
+ _data = res.json()
76
+
77
+ vote = Vote()
78
+ vote._load(_data, url = f"{self.url}/votes/{_data['id']}", headers = self.default_headers)
79
+ else:
80
+ res.raise_for_status()
81
+
82
+ # Aucune possibilité de supprimer un vote
83
+
84
+ """
85
+ PARTIS
86
+ """
87
+
88
+ def get_party(self, id: NSID) -> Party:
89
+ """
90
+ Récupère un parti politique.
91
+
92
+ ## Paramètres
93
+ id: `NSID`\n
94
+ ID du parti.
95
+
96
+ ## Renvoie
97
+ - `.Party`
98
+ """
99
+
100
+ id = NSID(id)
101
+ res = requests.get(f"{self.url}/parties/{id}", headers = self.default_headers)
102
+
103
+ if not res:
104
+ return None
105
+
106
+ _data = res.json()
107
+
108
+ party = Party(id)
109
+ party._load(_data, url = f"{self.url}/parties/{id}", headers = self.default_headers)
110
+
111
+ return party
112
+
113
+ def register_party(self, id: NSID, color: int, motto: str = None, scale: dict | Scale = {}) -> Party:
114
+ """
115
+ Enregistre un nouveau parti pour que ses députés puissent s'y présenter.
116
+
117
+ ## Paramètres
118
+ - id: `NSID`\n
119
+ ID de l'entreprise à laquelle correspond le parti
120
+ - color: `int`\n
121
+ Couleur du parti
122
+ - motto: `str, optional`\n
123
+ Devise du parti
124
+ - politiscales: `.Scale`\n
125
+ Résultats du parti au test Politiscales
126
+ """
127
+
128
+ payload = {
129
+ "color": color,
130
+ "motto": motto,
131
+ "scale": scale
132
+ }
133
+
134
+ res = requests.put(f"{self.url}/register_party?candidate={id}", headers = self.default_headers, json = _data)
135
+
136
+ if res.status_code == 200:
137
+ _data = res.json()
138
+
139
+ vote = Vote()
140
+ vote._load(_data, url = f"{self.url}/votes/{_data['id']}", headers = self.default_headers)
141
+ else:
142
+ res.raise_for_status()
@@ -0,0 +1,50 @@
1
+ import math
2
+ import time
3
+
4
+ EPOCH = 1577833200 # 1er Janvier 2020
5
+ PATIENTAGE_DATE = 1725141600 # 1er Septembre 2024
6
+ OPEN_DATE = 1756677600 # 1er Septembre 2025
7
+
8
+ MANDATE_DURATION = 2419200
9
+
10
+ def get_cycle(ts: int = round(time.time())):
11
+ if EPOCH <= ts < PATIENTAGE_DATE:
12
+ return 0
13
+ elif PATIENTAGE_DATE <= ts < OPEN_DATE:
14
+ return 1
15
+ elif OPEN_DATE <= ts:
16
+ return math.floor((ts - OPEN_DATE) / MANDATE_DURATION) + 2
17
+ else:
18
+ raise ValueError(f"Timestamp {ts} is out of range (must be greater than or equal to {EPOCH}).")
19
+
20
+ def get_day(ts: int = round(time.time())) -> int:
21
+ cycle = get_cycle(ts)
22
+
23
+ if cycle == 0:
24
+ return math.floor((ts - EPOCH) / 86400)
25
+ elif cycle == 1:
26
+ return math.floor((ts - PATIENTAGE_DATE) / 86400)
27
+ else:
28
+ return math.floor(((ts - OPEN_DATE) % MANDATE_DURATION) / 86400)
29
+
30
+ def get_phase(ts: int = round(time.time())) -> str:
31
+ cycle = get_cycle(ts)
32
+
33
+ if cycle < 2: # Les deux premiers cycles durent (beaucoup) plus longtemps qu'un cycle normal.
34
+ return 'undefined'
35
+
36
+ day = get_day(ts)
37
+
38
+ if day == 0:
39
+ return 'investiture' # Investiture du PR
40
+ elif 1 <= day < 24:
41
+ return 'paix' # Rien du tout
42
+ elif 24 <= day < 28:
43
+ return 'partials' # Élections législatives
44
+ elif 28 <= day < 52:
45
+ return 'paix' # Toujours rien
46
+ elif 52 <= day < 56:
47
+ return 'elections' # Élections législatives et présidentielles
48
+
49
+ else:
50
+ raise ValueError(f"Idk what happened but it seems that {day} is greater than 55...")
@@ -1,4 +1,3 @@
1
- import io
2
1
  import json
3
2
  import requests
4
3
  import typing
@@ -40,17 +39,18 @@ class NSID(str):
40
39
  if value.startswith("0x"):
41
40
  value = value[2:]
42
41
 
43
- instance = super(NSID, cls).__new__(cls, value.upper())
44
- return instance
42
+ interface = super(NSID, cls).__new__(cls, value.upper())
43
+ return interface
45
44
 
46
- class Instance:
45
+ class Interface:
47
46
  """
48
- Instance qui servira de base à toutes les instances.
47
+ Instance qui servira de base à toutes les interfaces.
49
48
  """
50
49
 
51
50
  def __init__(self, url: str, token: str = None):
52
51
  self.url = url
53
52
  self.token = token
53
+ self.zone = 20 # 10 = Serveur test, 20 = Serveur principal, 30 = Serveur de patientage, 40 = Scratch World
54
54
 
55
55
  self.default_headers = {
56
56
  "Authorization": f"Bearer {self.token}",
@@ -71,6 +71,24 @@ class Instance:
71
71
  except:
72
72
  utils.warn("NationDB is not responding.")
73
73
 
74
+ def alias(self, alias: NSID) -> typing.Self:
75
+ """
76
+ Duplique l'interface en se faisant passer pour une autre entité. Aucune erreur ne sera levée si l'entité n'existe pas (hormis les éventuels 401 ou 404 renvoyés par le serveur).
77
+
78
+ ## Paramètres
79
+ alias: `NSID`\n
80
+ ID de l'entité à simuler
81
+
82
+ ## Renvoie
83
+ - `self` avec le token de l'alias
84
+ """
85
+
86
+ alias = NSID(alias)
87
+
88
+ token = self.token + ':' + str(alias)
89
+
90
+ return self.__class__(self.url, token)
91
+
74
92
  def request_token(self, username: str, password: str) -> str | None:
75
93
  res = requests.post(f"{self.url}/auth/login", json = {
76
94
  "username": username,