mcserver-cli 3.0.0__py3-none-any.whl

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 (61) hide show
  1. mcserver/__init__.py +1 -0
  2. mcserver/__main__.py +3 -0
  3. mcserver/app.py +24 -0
  4. mcserver/caching.py +0 -0
  5. mcserver/cli.py +326 -0
  6. mcserver/commands/__init__.py +39 -0
  7. mcserver/commands/add_projects.py +7 -0
  8. mcserver/commands/import_setup.py +6 -0
  9. mcserver/commands/initialize_server.py +119 -0
  10. mcserver/commands/list_projects.py +6 -0
  11. mcserver/commands/op_grant.py +7 -0
  12. mcserver/commands/op_list.py +32 -0
  13. mcserver/commands/op_revoke.py +7 -0
  14. mcserver/commands/print_help.py +7 -0
  15. mcserver/commands/remove_projects.py +6 -0
  16. mcserver/commands/search_projects.py +7 -0
  17. mcserver/commands/server_status.py +6 -0
  18. mcserver/commands/show_projects.py +26 -0
  19. mcserver/commands/start_server.py +150 -0
  20. mcserver/commands/stop_server.py +46 -0
  21. mcserver/commands/update_projects.py +6 -0
  22. mcserver/commands/whitelist_add.py +7 -0
  23. mcserver/commands/whitelist_list.py +31 -0
  24. mcserver/commands/whitelist_remove.py +31 -0
  25. mcserver/commandsa.py +1297 -0
  26. mcserver/config.py +37 -0
  27. mcserver/constants.py +14 -0
  28. mcserver/fabricmc/meta.py +41 -0
  29. mcserver/legacyfabric/meta.py +37 -0
  30. mcserver/metadata.py +33 -0
  31. mcserver/minecraft/api.py +94 -0
  32. mcserver/minecraft/player_identity.py +21 -0
  33. mcserver/modrinth/api.py +657 -0
  34. mcserver/modrinth/modpack.py +101 -0
  35. mcserver/modrinth/project_records/metadata.py +0 -0
  36. mcserver/modrinth/project_records/relationships.py +203 -0
  37. mcserver/modrinth/project_records/shared.py +9 -0
  38. mcserver/modrinth/projects.py +80 -0
  39. mcserver/modrinth/shared.py +13 -0
  40. mcserver/modrinth/slug_index.py +36 -0
  41. mcserver/mojang/eula.py +28 -0
  42. mcserver/mojang/manifest.py +69 -0
  43. mcserver/mysterious.py +5 -0
  44. mcserver/networking.py +152 -0
  45. mcserver/papermc/api.py +81 -0
  46. mcserver/purpurmc/api.py +81 -0
  47. mcserver/quiltmc/meta.py +54 -0
  48. mcserver/resolver.py +164 -0
  49. mcserver/server_files/banned_ips.py +120 -0
  50. mcserver/server_files/banned_players.py +166 -0
  51. mcserver/server_files/ops.py +143 -0
  52. mcserver/server_files/properties.py +41 -0
  53. mcserver/server_files/usercaches.py +0 -0
  54. mcserver/server_files/whitelist.py +163 -0
  55. mcserver/shared.py +198 -0
  56. mcserver/state.py +77 -0
  57. mcserver_cli-3.0.0.dist-info/METADATA +63 -0
  58. mcserver_cli-3.0.0.dist-info/RECORD +61 -0
  59. mcserver_cli-3.0.0.dist-info/WHEEL +4 -0
  60. mcserver_cli-3.0.0.dist-info/entry_points.txt +2 -0
  61. mcserver_cli-3.0.0.dist-info/licenses/LICENSE +9 -0
@@ -0,0 +1,657 @@
1
+ # Modules
2
+ # Standard
3
+ import json
4
+ from typing import Literal, NotRequired, TypedDict
5
+
6
+ # MCServer
7
+ from .. import networking
8
+ from ..constants import modrinth_api_url
9
+
10
+
11
+ # TypedDict
12
+ # Project Version
13
+ class FileHashes(TypedDict):
14
+ sha512: NotRequired[str]
15
+ sha1: NotRequired[str]
16
+
17
+
18
+ class VersionFile(TypedDict):
19
+ file_hashes: FileHashes
20
+ download_url: str
21
+ filename: str
22
+ is_primary: bool
23
+ file_size: int
24
+ file_type: NotRequired[
25
+ Literal[
26
+ "required-resource-pack",
27
+ "optional-resource-pack",
28
+ "sources-jar",
29
+ "dev-jar",
30
+ "javadoc-jar",
31
+ "unknown",
32
+ "signature",
33
+ ]
34
+ | None
35
+ ]
36
+
37
+
38
+ class VersionDependency(TypedDict):
39
+ version_id: NotRequired[str | None]
40
+ project_id: NotRequired[str | None]
41
+ filename: NotRequired[str | None]
42
+ dependency_type: Literal["required", "optional", "incompatible", "embedded"]
43
+
44
+
45
+ class ProjectVersion(TypedDict):
46
+ version_name: NotRequired[str]
47
+ version_number: NotRequired[str]
48
+ changelog: NotRequired[str | None]
49
+ dependencies: NotRequired[list[VersionDependency]]
50
+ game_versions: NotRequired[list[str]]
51
+ version_type: NotRequired[Literal["release", "beta", "alpha"]]
52
+ loader_names: NotRequired[list[str]]
53
+ is_featured: NotRequired[bool]
54
+ status: NotRequired[
55
+ Literal["listed", "archived", "draft", "unlisted", "scheduled", "unknown"]
56
+ ]
57
+ requested_status: NotRequired[
58
+ Literal["listed", "archived", "draft", "unlisted"] | None
59
+ ]
60
+ version_id: str
61
+ project_id: str
62
+ author_id: str
63
+ published_time: str # Time format: ISO-8601
64
+ download_count: int
65
+ changelog_url: NotRequired[str | None]
66
+ files: list[VersionFile]
67
+
68
+
69
+ # Project Information
70
+ class DonationPlatform(TypedDict):
71
+ platform_id: NotRequired[str]
72
+ platform_name: NotRequired[str]
73
+ donation_url: NotRequired[str]
74
+
75
+
76
+ class ModeratorMessage(TypedDict):
77
+ message: NotRequired[str]
78
+ body: NotRequired[str | None]
79
+
80
+
81
+ class ProjectLicense(TypedDict):
82
+ license_id: NotRequired[str]
83
+ license_name: NotRequired[str]
84
+ license_url: NotRequired[str | None]
85
+
86
+
87
+ class GalleryImage(TypedDict):
88
+ image_url: str
89
+ is_featured: bool
90
+ image_title: NotRequired[str | None]
91
+ image_description: NotRequired[str | None]
92
+ created_time: str # Time format: ISO-8601
93
+ image_order: NotRequired[int]
94
+
95
+
96
+ class ProjectInformation(TypedDict):
97
+ project_slug: NotRequired[str]
98
+ project_title: NotRequired[str]
99
+ summary: NotRequired[str]
100
+ categories: NotRequired[list[str]]
101
+ client_side: NotRequired[Literal["required", "optional", "unsupported", "unknown"]]
102
+ server_side: NotRequired[Literal["required", "optional", "unsupported", "unknown"]]
103
+ description: NotRequired[str]
104
+ status: NotRequired[
105
+ Literal[
106
+ "approved",
107
+ "archived",
108
+ "rejected",
109
+ "draft",
110
+ "unlisted",
111
+ "processing",
112
+ "withheld",
113
+ "scheduled",
114
+ "private",
115
+ "unknown",
116
+ ]
117
+ ]
118
+ requested_status: NotRequired[
119
+ Literal["approved", "archived", "unlisted", "private", "draft"] | None
120
+ ]
121
+ additional_categories: NotRequired[list[str]]
122
+ issues_url: NotRequired[str | None]
123
+ source_url: NotRequired[str | None]
124
+ wiki_url: NotRequired[str | None]
125
+ discord_url: NotRequired[str | None]
126
+ donation_platforms: NotRequired[list[DonationPlatform]]
127
+ project_type: Literal["mod", "modpack", "resourcepack", "shader"]
128
+ download_count: int
129
+ icon_url: NotRequired[str | None]
130
+ icon_color: NotRequired[int | None]
131
+ thread_id: NotRequired[str]
132
+ monetization_status: NotRequired[
133
+ Literal["monetized", "demonetized", "force-demonetized"]
134
+ ]
135
+ project_id: str
136
+ team_id: str
137
+ description_url: NotRequired[str | None]
138
+ moderator_message: NotRequired[
139
+ ModeratorMessage | None
140
+ ] # NOTE: The docs is misleading
141
+ published_time: str # Time format: ISO-8601
142
+ last_updated_time: str # Time format: ISO-8601
143
+ approved_time: NotRequired[str | None] # Time format: ISO-8601
144
+ queued_time: NotRequired[str | None] # Time format: ISO-8601
145
+ follower_count: int
146
+ license: NotRequired[ProjectLicense]
147
+ version_ids: NotRequired[list[str]]
148
+ game_versions: NotRequired[list[str]]
149
+ loader_names: NotRequired[list[str]]
150
+ project_gallery: NotRequired[list[GalleryImage]]
151
+
152
+
153
+ class SearchHit(TypedDict):
154
+ project_slug: NotRequired[str]
155
+ project_title: NotRequired[str]
156
+ short_description: NotRequired[str]
157
+ categories: NotRequired[list[str]]
158
+ client_side: NotRequired[Literal["required", "optional", "unsupported", "unknown"]]
159
+ server_side: NotRequired[Literal["required", "optional", "unsupported", "unknown"]]
160
+ project_type: Literal["mod", "modpack", "resourcepack", "shader"]
161
+ download_count: int
162
+ icon_url: NotRequired[str | None]
163
+ icon_color: NotRequired[int | None]
164
+ thread_id: NotRequired[str]
165
+ monetization_status: NotRequired[
166
+ Literal["monetized", "demonetized", "force-demonetized"]
167
+ ]
168
+ project_id: str
169
+ all_project_types: list[str]
170
+ author_username: str
171
+ display_categories: NotRequired[list[str]]
172
+ game_versions: list[str]
173
+ follower_count: int
174
+ created_time: str # Time Format: ISO-8601
175
+ modified_time: str # Time Format: ISO-8601
176
+ latest_game_version: NotRequired[str]
177
+ license_id: str
178
+ gallery_images: NotRequired[list[str]]
179
+ featured_galeery: NotRequired[str | None]
180
+
181
+
182
+ class SearchResult(TypedDict):
183
+ hits: list[SearchHit]
184
+ offset: int
185
+ limit: int
186
+ total_hits: int
187
+
188
+
189
+ # Functions
190
+ def get_project_versions(
191
+ project_id: str,
192
+ loader_names: list[str] | str | None = None,
193
+ game_versions: list[str] | str | None = None,
194
+ featured: bool | None = None,
195
+ include_changelog: bool = True,
196
+ ) -> list[ProjectVersion]:
197
+ if isinstance(loader_names, str):
198
+ loader_names = [loader_names]
199
+
200
+ if isinstance(game_versions, str):
201
+ game_versions = [game_versions]
202
+
203
+ query_parameter = {
204
+ "loaders": json.dumps(loader_names),
205
+ "game_versions": json.dumps(game_versions),
206
+ "include_changelog": json.dumps(include_changelog),
207
+ }
208
+
209
+ # NOTE: 'featured' is trinary
210
+ if featured is not None:
211
+ query_parameter["featured"] = json.dumps(featured)
212
+
213
+ response = networking.request(
214
+ f"{modrinth_api_url}/v2/project/{project_id}/version", query=query_parameter
215
+ )
216
+ response_json = json.loads(response["text"])
217
+
218
+ project_versions = []
219
+ for version in response_json:
220
+ dependencies = []
221
+ if "dependencies" in version:
222
+ for dependency in version["dependencies"]:
223
+ version_dependency = {"dependency_type": dependency["dependency_type"]}
224
+
225
+ if "version_id" in dependency:
226
+ version_dependency["version_id"] = dependency["version_id"]
227
+ if "project_id" in dependency:
228
+ version_dependency["project_id"] = dependency["project_id"]
229
+ if "file_name" in dependency:
230
+ version_dependency["filename"] = dependency["file_name"]
231
+ dependencies.append(version_dependency)
232
+
233
+ files = []
234
+ for file in version["files"]:
235
+ # Overcomplicate now because yes
236
+ file_hashes = {}
237
+ for hash_algorithm, hash_value in file["hashes"].items():
238
+ if hash_algorithm in ["sha512", "sha1"]:
239
+ file_hashes[hash_algorithm] = hash_value
240
+
241
+ version_file = {
242
+ "file_hashes": file_hashes,
243
+ "download_url": file["url"],
244
+ "filename": file["filename"],
245
+ "is_primary": file["primary"],
246
+ "file_size": file["size"],
247
+ }
248
+
249
+ if "file_type" in file:
250
+ version_file["file_type"] = file["file_type"]
251
+ files.append(version_file)
252
+
253
+ project_version = {
254
+ "version_id": version["id"],
255
+ "project_id": version["project_id"],
256
+ "author_id": version["author_id"],
257
+ "published_time": version["date_published"],
258
+ "download_count": version["downloads"],
259
+ "files": files,
260
+ }
261
+
262
+ if "name" in version:
263
+ project_version["version_name"] = version["name"]
264
+
265
+ if "version_number" in version:
266
+ project_version["version_number"] = version["version_number"]
267
+
268
+ if "changelog" in version:
269
+ project_version["changelog"] = version["changelog"]
270
+
271
+ if "dependencies" in version:
272
+ project_version["dependencies"] = dependencies
273
+
274
+ if "game_versions" in version:
275
+ project_version["game_versions"] = version["game_versions"]
276
+
277
+ if "version_type" in version:
278
+ project_version["version_type"] = version["version_type"]
279
+
280
+ if "loaders" in version:
281
+ project_version["loader_names"] = version["loaders"]
282
+
283
+ if "featured" in version:
284
+ project_version["is_featured"] = version["featured"]
285
+
286
+ if "status" in version:
287
+ project_version["status"] = version["status"]
288
+
289
+ if "requested_status" in version:
290
+ project_version["requested_status"] = version["requested_status"]
291
+
292
+ if "changelog_url" in version:
293
+ project_version["changelog_url"] = version["changelog_url"]
294
+
295
+ project_versions.append(project_version)
296
+
297
+ return project_versions
298
+
299
+
300
+ def get_versions_from_hashes(
301
+ file_hashes: list[str] | str, hash_algorithm: Literal["sha1", "sha512"]
302
+ ) -> dict[str, ProjectVersion]:
303
+ if isinstance(file_hashes, str):
304
+ file_hashes = [file_hashes]
305
+
306
+ request_body = json.dumps(
307
+ {"hashes": file_hashes, "algorithm": hash_algorithm}
308
+ ).encode("utf-8")
309
+
310
+ response = networking.request(
311
+ f"{modrinth_api_url}/v2/version_files",
312
+ data=request_body,
313
+ headers={"Content-Type": "application/json"},
314
+ method="POST",
315
+ )
316
+
317
+ response_json = json.loads(response["text"])
318
+
319
+ hash_to_version = {}
320
+ for hash_value, version in response_json.items():
321
+ dependencies = []
322
+ if "dependencies" in version:
323
+ for dependency in version["dependencies"]:
324
+ version_dependency = {"dependency_type": dependency["dependency_type"]}
325
+
326
+ if "version_id" in dependency:
327
+ version_dependency["version_id"] = dependency["version_id"]
328
+
329
+ if "project_id" in dependency:
330
+ version_dependency["project_id"] = dependency["project_id"]
331
+
332
+ if "file_name" in dependency:
333
+ version_dependency["filename"] = dependency["file_name"]
334
+
335
+ dependencies.append(version_dependency)
336
+
337
+ files = []
338
+ for file in version["files"]:
339
+ # Overcomplicate now because yes
340
+ file_hash = {}
341
+ for hash_algorithm, file_hash_value in file["hashes"].items():
342
+ if hash_algorithm in ["sha512", "sha1"]:
343
+ file_hash[hash_algorithm] = file_hash_value
344
+
345
+ version_file = {
346
+ "file_hash": file_hash,
347
+ "download_url": file["url"],
348
+ "filename": file["filename"],
349
+ "is_primary": file["primary"],
350
+ "file_size": file["size"],
351
+ }
352
+
353
+ if "file_type" in file:
354
+ version_file["file_type"] = file["file_type"]
355
+ files.append(version_file)
356
+
357
+ project_version = {
358
+ "version_id": version["id"],
359
+ "project_id": version["project_id"],
360
+ "author_id": version["author_id"],
361
+ "published_time": version["date_published"],
362
+ "download_count": version["downloads"],
363
+ "files": files,
364
+ }
365
+
366
+ if "name" in version:
367
+ project_version["version_name"] = version["name"]
368
+
369
+ if "version_number" in version:
370
+ project_version["version_number"] = version["version_number"]
371
+
372
+ if "changelog" in version:
373
+ project_version["changelog"] = version["changelog"]
374
+
375
+ if "dependencies" in version:
376
+ project_version["dependencies"] = dependencies
377
+
378
+ if "game_versions" in version:
379
+ project_version["game_versions"] = version["game_versions"]
380
+
381
+ if "version_type" in version:
382
+ project_version["version_type"] = version["version_type"]
383
+
384
+ if "loaders" in version:
385
+ project_version["loader_names"] = version["loaders"]
386
+
387
+ if "featured" in version:
388
+ project_version["is_featured"] = version["featured"]
389
+
390
+ if "status" in version:
391
+ project_version["status"] = version["status"]
392
+
393
+ if "requested_status" in version:
394
+ project_version["requested_status"] = version["requested_status"]
395
+
396
+ if "changelog_url" in version:
397
+ project_version["changelog_url"] = version["changelog_url"]
398
+
399
+ hash_to_version[hash_value] = project_version
400
+
401
+ return hash_to_version
402
+
403
+
404
+ def get_project_information(project_ids: list[str] | str) -> list[ProjectInformation]:
405
+ if isinstance(project_ids, str):
406
+ project_ids = [project_ids]
407
+
408
+ query_parameters: dict[str, str] = {"ids": json.dumps(project_ids)}
409
+
410
+ response = networking.request(
411
+ f"{modrinth_api_url}/v2/projects", query=query_parameters
412
+ )
413
+ response_json = json.loads(response["text"])
414
+
415
+ project_informations: list[ProjectInformation] = []
416
+ for project in response_json:
417
+ client_side: Literal["required", "optional", "unsupported", "unknown"] = (
418
+ "unknown"
419
+ )
420
+ if "client_side" in project:
421
+ if project["client_side"] in [
422
+ "required",
423
+ "optional",
424
+ "unsupported",
425
+ "unknown",
426
+ ]:
427
+ client_side = project["client_side"]
428
+
429
+ server_side: Literal["required", "optional", "unsupported", "unknown"] = (
430
+ "unknown"
431
+ )
432
+ if "server_side" in project:
433
+ if project["server_side"] in [
434
+ "required",
435
+ "optional",
436
+ "unsupported",
437
+ "unknown",
438
+ ]:
439
+ server_side = project["server_side"]
440
+
441
+ status: Literal[
442
+ "approved",
443
+ "archived",
444
+ "rejected",
445
+ "draft",
446
+ "unlisted",
447
+ "processing",
448
+ "withheld",
449
+ "scheduled",
450
+ "private",
451
+ "unknown",
452
+ ] = "unknown"
453
+ if "status" in project:
454
+ if project["status"] in [
455
+ "approved",
456
+ "archived",
457
+ "rejected",
458
+ "draft",
459
+ "unlisted",
460
+ "processing",
461
+ "withheld",
462
+ "scheduled",
463
+ "private",
464
+ "unknown",
465
+ ]:
466
+ status = project["status"]
467
+
468
+ requested_status: (
469
+ Literal["approved", "archived", "unlisted", "private", "draft"] | None
470
+ ) = None
471
+ if "requested_status" in project:
472
+ requested_status_data = project["requested_status"]
473
+ if (
474
+ requested_status_data
475
+ in ["approved", "archived", "unlisted", "private", "draft"]
476
+ or requested_status_data is None
477
+ ):
478
+ requested_status = requested_status_data
479
+
480
+ monetization_status: Literal[
481
+ "monetized", "demonetized", "force-demonetized"
482
+ ] = "monetized"
483
+ if "monetization_status" in project:
484
+ if project["monetization_status"] in [
485
+ "monetized",
486
+ "demonetized",
487
+ "force-demonetized",
488
+ ]:
489
+ monetization_status = project["monetization_status"]
490
+
491
+ donation_platforms: list[DonationPlatform] = []
492
+ if "donation_urls" in project:
493
+ for platform in project["donation_urls"]:
494
+ donation_platform: DonationPlatform = {}
495
+
496
+ if "id" in platform:
497
+ donation_platform["platform_id"] = platform["id"]
498
+ if "platform" in platform:
499
+ donation_platform["platform_name"] = platform["platform"]
500
+ if "url" in platform:
501
+ donation_platform["donation_url"] = platform["url"]
502
+ donation_platforms.append(donation_platform)
503
+
504
+ moderator_message: ModeratorMessage | None = None
505
+ if "moderator_message" in project:
506
+ moderator_message_data = project["moderator_message"]
507
+ if moderator_message_data is None:
508
+ moderator_message = None
509
+ else:
510
+ # pls forgive me I was tired, maybe you fix it then
511
+ if moderator_message is None:
512
+ moderator_message = {}
513
+
514
+ if "message" in moderator_message_data:
515
+ moderator_message["message"] = moderator_message_data["message"]
516
+ if "body" in moderator_message_data:
517
+ moderator_message["body"] = moderator_message_data["body"]
518
+
519
+ project_license: ProjectLicense = {}
520
+ if "license" in project:
521
+ license_data = project["license"]
522
+ if "id" in license_data:
523
+ project_license["license_id"] = license_data["id"]
524
+ if "name" in license_data:
525
+ project_license["license_name"] = license_data["name"]
526
+ if "url" in license_data:
527
+ project_license["license_url"] = license_data["url"]
528
+
529
+ project_gallery: list[GalleryImage] = []
530
+ if "gallery" in project:
531
+ for image in project["gallery"]:
532
+ gallery_image: GalleryImage = {
533
+ "image_url": image["url"],
534
+ "is_featured": image["featured"],
535
+ "created_time": image["created"],
536
+ }
537
+
538
+ if "title" in image:
539
+ gallery_image["image_title"] = image["title"]
540
+ if "description" in image:
541
+ gallery_image["image_description"] = image["description"]
542
+ if "ordering" in image:
543
+ gallery_image["image_order"] = image["ordering"]
544
+ project_gallery.append(gallery_image)
545
+
546
+ project_information: ProjectInformation = {
547
+ "project_type": project["project_type"],
548
+ "download_count": project["downloads"],
549
+ "project_id": project["id"],
550
+ "team_id": project["team"],
551
+ "published_time": project["published"],
552
+ "last_updated_time": project["updated"],
553
+ "follower_count": project["followers"],
554
+ }
555
+
556
+ if "slug" in project:
557
+ project_information["project_slug"] = project["slug"]
558
+
559
+ if "title" in project:
560
+ project_information["project_title"] = project["title"]
561
+
562
+ if "description" in project:
563
+ project_information["summary"] = project["description"]
564
+
565
+ if "categories" in project:
566
+ project_information["categories"] = project["categories"]
567
+
568
+ if "client_side" in project:
569
+ project_information["client_side"] = client_side
570
+
571
+ if "server_side" in project:
572
+ project_information["server_side"] = server_side
573
+
574
+ if "body" in project:
575
+ project_information["description"] = project["body"]
576
+
577
+ if "status" in project:
578
+ project_information["status"] = status
579
+
580
+ if "requested_status" in project:
581
+ project_information["requested_status"] = requested_status
582
+
583
+ if "additional_categories" in project:
584
+ project_information["additional_categories"] = project[
585
+ "additional_categories"
586
+ ]
587
+
588
+ if "issues_url" in project:
589
+ project_information["issues_url"] = project["issues_url"]
590
+
591
+ if "source_url" in project:
592
+ project_information["source_url"] = project["source_url"]
593
+
594
+ if "wiki_url" in project:
595
+ project_information["wiki_url"] = project["wiki_url"]
596
+
597
+ if "discord_url" in project:
598
+ project_information["discord_url"] = project["discord_url"]
599
+
600
+ if "donation_urls" in project:
601
+ project_information["donation_platforms"] = donation_platforms
602
+
603
+ if "icon_url" in project:
604
+ project_information["icon_url"] = project["icon_url"]
605
+
606
+ if "color" in project:
607
+ project_information["icon_color"] = project["color"]
608
+
609
+ if "thread_id" in project:
610
+ project_information["thread_id"] = project["thread_id"]
611
+
612
+ if "monetization_status" in project:
613
+ project_information["monetization_status"] = monetization_status
614
+
615
+ if "body_url" in project:
616
+ project_information["description_url"] = project["body_url"]
617
+
618
+ if "moderator_message" in project:
619
+ project_information["moderator_message"] = moderator_message
620
+
621
+ if "approved" in project:
622
+ project_information["approved_time"] = project["approved"]
623
+
624
+ if "queued" in project:
625
+ project_information["queued_time"] = project["queued"]
626
+
627
+ if "license" in project:
628
+ project_information["license"] = project_license
629
+
630
+ if "versions" in project:
631
+ project_information["version_ids"] = project["versions"]
632
+
633
+ if "game_versions" in project:
634
+ project_information["game_versions"] = project["game_versions"]
635
+
636
+ if "loaders" in project:
637
+ project_information["loader_names"] = project["loaders"]
638
+
639
+ if "gallery" in project:
640
+ project_information["project_gallery"] = project_gallery
641
+
642
+ project_informations.append(project_information)
643
+
644
+ return project_informations
645
+
646
+
647
+ def search_projects(
648
+ query: str,
649
+ facets: list[list[str]],
650
+ index: Literal[
651
+ "relevance", "downloads", "follows", "newest", "updated"
652
+ ] = "relevance",
653
+ offset: int = 0,
654
+ limit: int = 10,
655
+ ) -> SearchResult:
656
+ search_result: SearchResult = {}
657
+ return search_result