altvmasterlist.masterlist
1#!/usr/bin/env python3 2from altvmasterlist import exceptions as error 3from altvmasterlist import enum as enum 4from urllib.parse import quote 5from dataclasses import dataclass, field 6from typing import Optional 7from io import StringIO 8from re import compile 9from enum import Enum 10import requests 11import logging 12import sys 13 14 15"""You can find the masterlist api docs here: https://docs.altv.mp/articles/master_list_api.html""" 16logger = logging.getLogger(__name__) 17session = requests.session() 18 19 20def request(url: str, server: any = None) -> dict | None: 21 """This is the common request function to fetch remote data. 22 23 Args: 24 url (str): The Url to fetch. 25 server (Server): An alt:V masterlist Server object. 26 27 Returns: 28 None: When an error occurred. But exceptions will still be logged! 29 json: As data 30 31 Raises: 32 FetchError: there was an error while getting the data 33 """ 34 # Use the User-Agent: AltPublicAgent, because some servers protect their CDN with 35 # a simple User-Agent check e.g. https://luckyv.de did that before 36 session.headers.clear() 37 38 # if we get a request for a server that is not using 39 # a cdn then set the same headers as the alt:V client while connecting 40 # otherwise set a user-agent and Content-Type for the api 41 if server and "http://" in url and not server.useCdn: 42 session.headers = enum.RequestHeaders(server).to_dict() 43 else: 44 session.headers = { 45 "User-Agent": enum.Extra.user_agent.value, 46 "Content-Type": "application/json; charset=utf-8", 47 } 48 49 try: 50 api_request = session.get(url, timeout=5) 51 52 if not api_request.ok: 53 raise error.FetchError(f"The request returned an error. {url}") 54 else: 55 return api_request.json() 56 except Exception as e: 57 logger.error(e) 58 return None 59 60 61@dataclass 62class Server: 63 playersCount: int = field(default=0, metadata={"description": "Current player count"}) 64 maxPlayersCount: int = field(default=0, metadata={"description": "Player limit"}) 65 passworded: bool = field(default=False, metadata={"description": "Password protected"}) 66 port: int = field(default=0, metadata={"description": "Server game port"}) 67 language: str = field(default="en", metadata={"description": "Two letter country code"}) 68 useEarlyAuth: bool = field(default=False, metadata={"description": "Server is using early auth (https://docs.altv.mp/articles/earlyauth.html)"}) 69 earlyAuthUrl: str = field(default="", metadata={"description": "Early auth URL (usually a login screen)"}) 70 useCdn: bool = field(default=False, metadata={"description": "Server is using a CDN (https://docs.altv.mp/articles/cdn.html)"}) 71 cdnUrl: str = field(default="", metadata={"description": "CDN URL"}) 72 useVoiceChat: bool = field(default=False, metadata={"description": "Server is using the built-in voice chat (https://docs.altv.mp/articles/voice.html)"}) 73 version: str = field(default="", metadata={"description": "Server version"}) 74 branch: str = field(default="", metadata={"description": "Server branch (release, rc, dev)"}) 75 available: bool = field(default=False, metadata={"description": "Server is online"}) 76 banned: bool = field(default=False) 77 name: str = field(default="", metadata={"description": "Server name"}) 78 publicId: str = field(default=None, metadata={"description": "The server ID."}) 79 vanityUrl: str = field(default="") 80 website: str = field(default="", metadata={"description": "Server website"}) 81 gameMode: str = field(default="", metadata={"description": "Gamemode provided by the server"}) 82 description: str = field(default="", metadata={"description": "Description provided by the server"}) 83 tags: str = field(default="", metadata={"description": "Tags provided by the server"}) 84 lastTimeUpdate: str = field(default="", metadata={"description": "Time string with format 2024-02-12T16:22:24.195392493Z"}) 85 verified: bool = field(default=False, metadata={"description": "alt:V verified server"}) 86 promoted: bool = field(default=False, metadata={"description": "Promoted server"}) 87 visible: bool = field(default=False, metadata={"description": "Visible in server list"}) 88 hasCustomSkin: bool = field(default=False, metadata={"description": "Defines if the server has a custom launcher skin"}) 89 bannerUrl: str = field(default="") 90 address: str = field(default="", metadata={"description": "Connection address for the client, can be URL + port or IP + port"}) 91 group: Optional[enum.Group] = field(default=None, metadata={"description": "Server group info"}) 92 masterlist_icon_url: Optional[str] = field(default=None, metadata={"description": "Server icon shown on masterlist"}) 93 masterlist_banner_url: Optional[str] = field(default=None, metadata={"description": "Banner shown when you click on the server in the masterlist"}) 94 95 def __post_init__(self): 96 self.fetch_data() 97 98 def fetch_data(self): 99 try: 100 temp_data = request(enum.MasterlistUrls.specific_server.value.format(self.publicId)) 101 except error.FetchError as e: 102 logger.error(f"there was an error fetching server data: {self.publicId} {e}") 103 return 104 105 if temp_data: 106 for key, value in temp_data.items(): 107 if hasattr(self, key): 108 setattr(self, key, value) 109 if "group" in temp_data and temp_data["group"]: 110 self.group = enum.Group(**temp_data["group"]) 111 112 def __init__(self, server_id: str, no_fetch: bool = False) -> None: 113 self.publicId = server_id 114 if not no_fetch: 115 self.fetch_data() 116 117 def update(self) -> None: 118 """Update the server data using the api.""" 119 self.__post_init__() 120 121 def get_max(self, time: str = "1d") -> dict | None: 122 """Maximum - Returns maximum data about the specified server (TIME = 1d, 7d, 31d) 123 124 Args: 125 time (str): The timerange of the data. Can be 1d, 7d, 31d. 126 127 Returns: 128 None: When an error occurs 129 dict: The maximum player data 130 131 Raises: 132 FetchError: there was an error while getting the data 133 NoPublicID: the server has no publicID 134 """ 135 if not self.publicId: 136 logger.warning("server got no masterlist publicID") 137 raise error.NoPublicID(f"The server got no publicID") 138 else: 139 try: 140 tmp_data = request(enum.MasterlistUrls.specific_server_maximum.value.format(self.publicId, time)) 141 return tmp_data 142 except error.FetchError as e: 143 logger.error(f"there was an error while getting max stats: {e}") 144 raise error.FetchError(f"there was an error while getting max stats: {e}") 145 146 def get_avg( 147 self, time: str = "1d", return_result: bool = False 148 ) -> dict | int | None: 149 """Averages - Returns averages data about the specified server (TIME = 1d, 7d, 31d) 150 151 Args: 152 time (str): The timerange of the data. Can be 1d, 7d, 31d. 153 return_result (bool): Define if you want the overall average. 154 155 Returns: 156 None: When an error occurs 157 dict: The maximum player data 158 int: Overall average of defined timerange 159 160 Raises: 161 FetchError: there was an error while getting the data 162 NoPublicID: the server has no publicID 163 """ 164 if not self.publicId: 165 logger.warning("server got not masterlist publicID") 166 raise error.NoPublicID(f"The server got no publicID") 167 else: 168 average_data = request( 169 enum.MasterlistUrls.specific_server_average.value.format(self.publicId, time) 170 ) 171 172 if not average_data: 173 raise error.FetchError(f"There was an error while fetching data for {self.publicId} {time} {return_result}") 174 175 if return_result: 176 players_all = 0 177 for entry in average_data: 178 players_all = players_all + entry["c"] 179 result = players_all / len(average_data) 180 return round(result) 181 else: 182 return average_data 183 184 @property 185 def connect_json(self) -> dict | None: 186 """This function fetched the connect.json of an alt:V server. 187 188 Returns: 189 None: When an error occurred. But exceptions will still be logged! 190 dict: The connect.json 191 192 Raises: 193 FetchError: there was an error while getting the data 194 """ 195 if not self.available or self.passworded: 196 raise error.FetchError(f"{self.publicId} is offline or password protected.") 197 198 if self.publicId: 199 if not self.useCdn: 200 # server is on masterlist but not using a cdn 201 cdn_request = request(f"http://{self.address}/connect.json", self) 202 else: 203 # server is on masterlist and using a cdn 204 match self.cdnUrl: 205 case _ if ":80" in self.cdnUrl: 206 cdn_request = request( 207 f"http://{self.cdnUrl.replace(':80', '')}/connect.json", self 208 ) 209 case _ if ":443" in self.cdnUrl: 210 cdn_request = request( 211 f"https://{self.cdnUrl.replace(':443', '')}/connect.json", self 212 ) 213 case _: 214 cdn_request = request(f"{self.cdnUrl}/connect.json", self) 215 else: 216 # server is not on masterlist 217 logger.info("getting connect.json by ip") 218 cdn_request = request(f"{self.address}:{self.port}/connect.json", self) 219 220 if cdn_request is None: 221 raise error.FetchError(f"There was an error while fetching connect.json for {self.publicId}") 222 else: 223 return cdn_request 224 225 @property 226 def permissions(self) -> enum.Permissions | None: 227 """This function returns the Permissions defined by the server. https://docs.altv.mp/articles/permissions.html 228 229 Returns: 230 None: When an error occurred. But exceptions will still be logged! 231 Permissions: The permissions of the server. 232 233 Raises: 234 FetchError: there was an error while getting the data 235 """ 236 237 class Permission(Enum): 238 screen_capture = "Screen Capture" 239 webrtc = "WebRTC" 240 clipboard_access = "Clipboard Access" 241 optional = "optional-permissions" 242 required = "required-permissions" 243 244 try: 245 connect_json = self.connect_json 246 except error.FetchError as e: 247 logger.error(e) 248 raise error.FetchError(f"couln't get permissions {e} ") 249 250 optional = connect_json[Permission.optional.value] 251 required = connect_json[Permission.required.value] 252 253 permissions = enum.Permissions() 254 255 # Define a list of permission attributes to check 256 permission_keys = [ 257 "screen_capture", 258 "webrtc", 259 "clipboard_access" 260 ] 261 262 # Assign values for optional permissions 263 for key in permission_keys: 264 if key in optional: 265 setattr(permissions.Optional, key, optional[key]) 266 if key in required: 267 setattr(permissions.Required, key, required[key]) 268 269 return permissions 270 271 def get_dtc_url(self, password=None) -> str | None: 272 """This function gets the direct connect protocol url of an alt:V Server. 273 (https://docs.altv.mp/articles/connectprotocol.html) 274 275 Args: 276 password (str): The password of the server. 277 278 Returns: 279 None: When an error occurred. But exceptions will still be logged! 280 str: The direct connect protocol url. 281 """ 282 with StringIO() as dtc_url: 283 if self.useCdn: 284 tmp_url = quote(self.cdnUrl, safe='') 285 if "http" not in self.cdnUrl: 286 dtc_url.write(f"altv://connect/http://{tmp_url}") 287 else: 288 dtc_url.write(f"altv://connect/{tmp_url}") 289 else: 290 dtc_url.write(f"altv://connect/{quote(self.address, safe='')}") 291 292 if self.passworded and password is None: 293 logger.warning( 294 "Your server is password protected but you did not supply a password for the Direct Connect Url." 295 ) 296 if password is not None: 297 dtc_url.write(f"?password={quote(password, safe='')}") 298 299 return dtc_url.getvalue() 300 301 302def get_server_stats() -> dict | None: 303 """Statistics - Player Count across all servers & The amount of servers online 304 305 Returns: 306 None: When an error occurs 307 dict: The stats 308 309 Raises: 310 FetchError: there was an error while getting the data 311 """ 312 try: 313 tmp_data = request(enum.MasterlistUrls.all_server_stats.value) 314 return tmp_data 315 except error.FetchError as e: 316 logger.error(f"error while getting server stats: {e}") 317 raise error.FetchError(f"error while getting server stats: {e}") 318 319 320def get_servers() -> list[Server] | None: 321 """Generates a list of all servers that are currently online. 322 Note that the server objects returned are not complete! 323 324 Returns: 325 None: When an error occurs 326 list: List object that contains all servers. 327 328 Raises: 329 FetchError: there was an error while getting the data 330 """ 331 return_servers = [] 332 try: 333 servers = request(enum.MasterlistUrls.all_servers.value) 334 except error.FetchError as e: 335 raise error.FetchError(f"failed to get servers: {e}") 336 337 server_attributes = [ 338 "playersCount", "maxPlayersCount", "passworded", "language", 339 "useEarlyAuth", "earlyAuthUrl", "useCdn", "cdnUrl", "useVoiceChat", 340 "version", "branch", "available", "banned", "name", "publicId", 341 "vanityUrl", "website", "gameMode", "description", "tags", 342 "lastTimeUpdate", "verified", "promoted", "visible", "hasCustomSkin", 343 "bannerUrl", "address", "group", "masterlist_icon_url", 344 "masterlist_banner_url" 345 ] 346 347 for server in servers: 348 tmp_server = Server(server["publicId"], no_fetch=True) 349 for attr in server_attributes: 350 setattr(tmp_server, attr, server[attr]) 351 return_servers.append(tmp_server) 352 353 return return_servers 354 355 356def validate_id(server_id: any) -> bool: 357 """Validate a server id 358 359 Args: 360 server_id (any): The id you want to check. 361 362 Returns: 363 bool: True = valid, False = invalid 364 """ 365 regex = compile(r"^[\da-zA-Z]{7}$") 366 return isinstance(server_id, str) and regex.match(server_id) is not None 367 368 369if __name__ == "__main__": 370 print("This is a Module!") 371 sys.exit()
21def request(url: str, server: any = None) -> dict | None: 22 """This is the common request function to fetch remote data. 23 24 Args: 25 url (str): The Url to fetch. 26 server (Server): An alt:V masterlist Server object. 27 28 Returns: 29 None: When an error occurred. But exceptions will still be logged! 30 json: As data 31 32 Raises: 33 FetchError: there was an error while getting the data 34 """ 35 # Use the User-Agent: AltPublicAgent, because some servers protect their CDN with 36 # a simple User-Agent check e.g. https://luckyv.de did that before 37 session.headers.clear() 38 39 # if we get a request for a server that is not using 40 # a cdn then set the same headers as the alt:V client while connecting 41 # otherwise set a user-agent and Content-Type for the api 42 if server and "http://" in url and not server.useCdn: 43 session.headers = enum.RequestHeaders(server).to_dict() 44 else: 45 session.headers = { 46 "User-Agent": enum.Extra.user_agent.value, 47 "Content-Type": "application/json; charset=utf-8", 48 } 49 50 try: 51 api_request = session.get(url, timeout=5) 52 53 if not api_request.ok: 54 raise error.FetchError(f"The request returned an error. {url}") 55 else: 56 return api_request.json() 57 except Exception as e: 58 logger.error(e) 59 return None
This is the common request function to fetch remote data.
Args: url (str): The Url to fetch. server (Server): An alt:V masterlist Server object.
Returns: None: When an error occurred. But exceptions will still be logged! json: As data
Raises: FetchError: there was an error while getting the data
62@dataclass 63class Server: 64 playersCount: int = field(default=0, metadata={"description": "Current player count"}) 65 maxPlayersCount: int = field(default=0, metadata={"description": "Player limit"}) 66 passworded: bool = field(default=False, metadata={"description": "Password protected"}) 67 port: int = field(default=0, metadata={"description": "Server game port"}) 68 language: str = field(default="en", metadata={"description": "Two letter country code"}) 69 useEarlyAuth: bool = field(default=False, metadata={"description": "Server is using early auth (https://docs.altv.mp/articles/earlyauth.html)"}) 70 earlyAuthUrl: str = field(default="", metadata={"description": "Early auth URL (usually a login screen)"}) 71 useCdn: bool = field(default=False, metadata={"description": "Server is using a CDN (https://docs.altv.mp/articles/cdn.html)"}) 72 cdnUrl: str = field(default="", metadata={"description": "CDN URL"}) 73 useVoiceChat: bool = field(default=False, metadata={"description": "Server is using the built-in voice chat (https://docs.altv.mp/articles/voice.html)"}) 74 version: str = field(default="", metadata={"description": "Server version"}) 75 branch: str = field(default="", metadata={"description": "Server branch (release, rc, dev)"}) 76 available: bool = field(default=False, metadata={"description": "Server is online"}) 77 banned: bool = field(default=False) 78 name: str = field(default="", metadata={"description": "Server name"}) 79 publicId: str = field(default=None, metadata={"description": "The server ID."}) 80 vanityUrl: str = field(default="") 81 website: str = field(default="", metadata={"description": "Server website"}) 82 gameMode: str = field(default="", metadata={"description": "Gamemode provided by the server"}) 83 description: str = field(default="", metadata={"description": "Description provided by the server"}) 84 tags: str = field(default="", metadata={"description": "Tags provided by the server"}) 85 lastTimeUpdate: str = field(default="", metadata={"description": "Time string with format 2024-02-12T16:22:24.195392493Z"}) 86 verified: bool = field(default=False, metadata={"description": "alt:V verified server"}) 87 promoted: bool = field(default=False, metadata={"description": "Promoted server"}) 88 visible: bool = field(default=False, metadata={"description": "Visible in server list"}) 89 hasCustomSkin: bool = field(default=False, metadata={"description": "Defines if the server has a custom launcher skin"}) 90 bannerUrl: str = field(default="") 91 address: str = field(default="", metadata={"description": "Connection address for the client, can be URL + port or IP + port"}) 92 group: Optional[enum.Group] = field(default=None, metadata={"description": "Server group info"}) 93 masterlist_icon_url: Optional[str] = field(default=None, metadata={"description": "Server icon shown on masterlist"}) 94 masterlist_banner_url: Optional[str] = field(default=None, metadata={"description": "Banner shown when you click on the server in the masterlist"}) 95 96 def __post_init__(self): 97 self.fetch_data() 98 99 def fetch_data(self): 100 try: 101 temp_data = request(enum.MasterlistUrls.specific_server.value.format(self.publicId)) 102 except error.FetchError as e: 103 logger.error(f"there was an error fetching server data: {self.publicId} {e}") 104 return 105 106 if temp_data: 107 for key, value in temp_data.items(): 108 if hasattr(self, key): 109 setattr(self, key, value) 110 if "group" in temp_data and temp_data["group"]: 111 self.group = enum.Group(**temp_data["group"]) 112 113 def __init__(self, server_id: str, no_fetch: bool = False) -> None: 114 self.publicId = server_id 115 if not no_fetch: 116 self.fetch_data() 117 118 def update(self) -> None: 119 """Update the server data using the api.""" 120 self.__post_init__() 121 122 def get_max(self, time: str = "1d") -> dict | None: 123 """Maximum - Returns maximum data about the specified server (TIME = 1d, 7d, 31d) 124 125 Args: 126 time (str): The timerange of the data. Can be 1d, 7d, 31d. 127 128 Returns: 129 None: When an error occurs 130 dict: The maximum player data 131 132 Raises: 133 FetchError: there was an error while getting the data 134 NoPublicID: the server has no publicID 135 """ 136 if not self.publicId: 137 logger.warning("server got no masterlist publicID") 138 raise error.NoPublicID(f"The server got no publicID") 139 else: 140 try: 141 tmp_data = request(enum.MasterlistUrls.specific_server_maximum.value.format(self.publicId, time)) 142 return tmp_data 143 except error.FetchError as e: 144 logger.error(f"there was an error while getting max stats: {e}") 145 raise error.FetchError(f"there was an error while getting max stats: {e}") 146 147 def get_avg( 148 self, time: str = "1d", return_result: bool = False 149 ) -> dict | int | None: 150 """Averages - Returns averages data about the specified server (TIME = 1d, 7d, 31d) 151 152 Args: 153 time (str): The timerange of the data. Can be 1d, 7d, 31d. 154 return_result (bool): Define if you want the overall average. 155 156 Returns: 157 None: When an error occurs 158 dict: The maximum player data 159 int: Overall average of defined timerange 160 161 Raises: 162 FetchError: there was an error while getting the data 163 NoPublicID: the server has no publicID 164 """ 165 if not self.publicId: 166 logger.warning("server got not masterlist publicID") 167 raise error.NoPublicID(f"The server got no publicID") 168 else: 169 average_data = request( 170 enum.MasterlistUrls.specific_server_average.value.format(self.publicId, time) 171 ) 172 173 if not average_data: 174 raise error.FetchError(f"There was an error while fetching data for {self.publicId} {time} {return_result}") 175 176 if return_result: 177 players_all = 0 178 for entry in average_data: 179 players_all = players_all + entry["c"] 180 result = players_all / len(average_data) 181 return round(result) 182 else: 183 return average_data 184 185 @property 186 def connect_json(self) -> dict | None: 187 """This function fetched the connect.json of an alt:V server. 188 189 Returns: 190 None: When an error occurred. But exceptions will still be logged! 191 dict: The connect.json 192 193 Raises: 194 FetchError: there was an error while getting the data 195 """ 196 if not self.available or self.passworded: 197 raise error.FetchError(f"{self.publicId} is offline or password protected.") 198 199 if self.publicId: 200 if not self.useCdn: 201 # server is on masterlist but not using a cdn 202 cdn_request = request(f"http://{self.address}/connect.json", self) 203 else: 204 # server is on masterlist and using a cdn 205 match self.cdnUrl: 206 case _ if ":80" in self.cdnUrl: 207 cdn_request = request( 208 f"http://{self.cdnUrl.replace(':80', '')}/connect.json", self 209 ) 210 case _ if ":443" in self.cdnUrl: 211 cdn_request = request( 212 f"https://{self.cdnUrl.replace(':443', '')}/connect.json", self 213 ) 214 case _: 215 cdn_request = request(f"{self.cdnUrl}/connect.json", self) 216 else: 217 # server is not on masterlist 218 logger.info("getting connect.json by ip") 219 cdn_request = request(f"{self.address}:{self.port}/connect.json", self) 220 221 if cdn_request is None: 222 raise error.FetchError(f"There was an error while fetching connect.json for {self.publicId}") 223 else: 224 return cdn_request 225 226 @property 227 def permissions(self) -> enum.Permissions | None: 228 """This function returns the Permissions defined by the server. https://docs.altv.mp/articles/permissions.html 229 230 Returns: 231 None: When an error occurred. But exceptions will still be logged! 232 Permissions: The permissions of the server. 233 234 Raises: 235 FetchError: there was an error while getting the data 236 """ 237 238 class Permission(Enum): 239 screen_capture = "Screen Capture" 240 webrtc = "WebRTC" 241 clipboard_access = "Clipboard Access" 242 optional = "optional-permissions" 243 required = "required-permissions" 244 245 try: 246 connect_json = self.connect_json 247 except error.FetchError as e: 248 logger.error(e) 249 raise error.FetchError(f"couln't get permissions {e} ") 250 251 optional = connect_json[Permission.optional.value] 252 required = connect_json[Permission.required.value] 253 254 permissions = enum.Permissions() 255 256 # Define a list of permission attributes to check 257 permission_keys = [ 258 "screen_capture", 259 "webrtc", 260 "clipboard_access" 261 ] 262 263 # Assign values for optional permissions 264 for key in permission_keys: 265 if key in optional: 266 setattr(permissions.Optional, key, optional[key]) 267 if key in required: 268 setattr(permissions.Required, key, required[key]) 269 270 return permissions 271 272 def get_dtc_url(self, password=None) -> str | None: 273 """This function gets the direct connect protocol url of an alt:V Server. 274 (https://docs.altv.mp/articles/connectprotocol.html) 275 276 Args: 277 password (str): The password of the server. 278 279 Returns: 280 None: When an error occurred. But exceptions will still be logged! 281 str: The direct connect protocol url. 282 """ 283 with StringIO() as dtc_url: 284 if self.useCdn: 285 tmp_url = quote(self.cdnUrl, safe='') 286 if "http" not in self.cdnUrl: 287 dtc_url.write(f"altv://connect/http://{tmp_url}") 288 else: 289 dtc_url.write(f"altv://connect/{tmp_url}") 290 else: 291 dtc_url.write(f"altv://connect/{quote(self.address, safe='')}") 292 293 if self.passworded and password is None: 294 logger.warning( 295 "Your server is password protected but you did not supply a password for the Direct Connect Url." 296 ) 297 if password is not None: 298 dtc_url.write(f"?password={quote(password, safe='')}") 299 300 return dtc_url.getvalue()
99 def fetch_data(self): 100 try: 101 temp_data = request(enum.MasterlistUrls.specific_server.value.format(self.publicId)) 102 except error.FetchError as e: 103 logger.error(f"there was an error fetching server data: {self.publicId} {e}") 104 return 105 106 if temp_data: 107 for key, value in temp_data.items(): 108 if hasattr(self, key): 109 setattr(self, key, value) 110 if "group" in temp_data and temp_data["group"]: 111 self.group = enum.Group(**temp_data["group"])
118 def update(self) -> None: 119 """Update the server data using the api.""" 120 self.__post_init__()
Update the server data using the api.
122 def get_max(self, time: str = "1d") -> dict | None: 123 """Maximum - Returns maximum data about the specified server (TIME = 1d, 7d, 31d) 124 125 Args: 126 time (str): The timerange of the data. Can be 1d, 7d, 31d. 127 128 Returns: 129 None: When an error occurs 130 dict: The maximum player data 131 132 Raises: 133 FetchError: there was an error while getting the data 134 NoPublicID: the server has no publicID 135 """ 136 if not self.publicId: 137 logger.warning("server got no masterlist publicID") 138 raise error.NoPublicID(f"The server got no publicID") 139 else: 140 try: 141 tmp_data = request(enum.MasterlistUrls.specific_server_maximum.value.format(self.publicId, time)) 142 return tmp_data 143 except error.FetchError as e: 144 logger.error(f"there was an error while getting max stats: {e}") 145 raise error.FetchError(f"there was an error while getting max stats: {e}")
Maximum - Returns maximum data about the specified server (TIME = 1d, 7d, 31d)
Args: time (str): The timerange of the data. Can be 1d, 7d, 31d.
Returns: None: When an error occurs dict: The maximum player data
Raises: FetchError: there was an error while getting the data NoPublicID: the server has no publicID
147 def get_avg( 148 self, time: str = "1d", return_result: bool = False 149 ) -> dict | int | None: 150 """Averages - Returns averages data about the specified server (TIME = 1d, 7d, 31d) 151 152 Args: 153 time (str): The timerange of the data. Can be 1d, 7d, 31d. 154 return_result (bool): Define if you want the overall average. 155 156 Returns: 157 None: When an error occurs 158 dict: The maximum player data 159 int: Overall average of defined timerange 160 161 Raises: 162 FetchError: there was an error while getting the data 163 NoPublicID: the server has no publicID 164 """ 165 if not self.publicId: 166 logger.warning("server got not masterlist publicID") 167 raise error.NoPublicID(f"The server got no publicID") 168 else: 169 average_data = request( 170 enum.MasterlistUrls.specific_server_average.value.format(self.publicId, time) 171 ) 172 173 if not average_data: 174 raise error.FetchError(f"There was an error while fetching data for {self.publicId} {time} {return_result}") 175 176 if return_result: 177 players_all = 0 178 for entry in average_data: 179 players_all = players_all + entry["c"] 180 result = players_all / len(average_data) 181 return round(result) 182 else: 183 return average_data
Averages - Returns averages data about the specified server (TIME = 1d, 7d, 31d)
Args: time (str): The timerange of the data. Can be 1d, 7d, 31d. return_result (bool): Define if you want the overall average.
Returns: None: When an error occurs dict: The maximum player data int: Overall average of defined timerange
Raises: FetchError: there was an error while getting the data NoPublicID: the server has no publicID
185 @property 186 def connect_json(self) -> dict | None: 187 """This function fetched the connect.json of an alt:V server. 188 189 Returns: 190 None: When an error occurred. But exceptions will still be logged! 191 dict: The connect.json 192 193 Raises: 194 FetchError: there was an error while getting the data 195 """ 196 if not self.available or self.passworded: 197 raise error.FetchError(f"{self.publicId} is offline or password protected.") 198 199 if self.publicId: 200 if not self.useCdn: 201 # server is on masterlist but not using a cdn 202 cdn_request = request(f"http://{self.address}/connect.json", self) 203 else: 204 # server is on masterlist and using a cdn 205 match self.cdnUrl: 206 case _ if ":80" in self.cdnUrl: 207 cdn_request = request( 208 f"http://{self.cdnUrl.replace(':80', '')}/connect.json", self 209 ) 210 case _ if ":443" in self.cdnUrl: 211 cdn_request = request( 212 f"https://{self.cdnUrl.replace(':443', '')}/connect.json", self 213 ) 214 case _: 215 cdn_request = request(f"{self.cdnUrl}/connect.json", self) 216 else: 217 # server is not on masterlist 218 logger.info("getting connect.json by ip") 219 cdn_request = request(f"{self.address}:{self.port}/connect.json", self) 220 221 if cdn_request is None: 222 raise error.FetchError(f"There was an error while fetching connect.json for {self.publicId}") 223 else: 224 return cdn_request
This function fetched the connect.json of an alt:V server.
Returns: None: When an error occurred. But exceptions will still be logged! dict: The connect.json
Raises: FetchError: there was an error while getting the data
226 @property 227 def permissions(self) -> enum.Permissions | None: 228 """This function returns the Permissions defined by the server. https://docs.altv.mp/articles/permissions.html 229 230 Returns: 231 None: When an error occurred. But exceptions will still be logged! 232 Permissions: The permissions of the server. 233 234 Raises: 235 FetchError: there was an error while getting the data 236 """ 237 238 class Permission(Enum): 239 screen_capture = "Screen Capture" 240 webrtc = "WebRTC" 241 clipboard_access = "Clipboard Access" 242 optional = "optional-permissions" 243 required = "required-permissions" 244 245 try: 246 connect_json = self.connect_json 247 except error.FetchError as e: 248 logger.error(e) 249 raise error.FetchError(f"couln't get permissions {e} ") 250 251 optional = connect_json[Permission.optional.value] 252 required = connect_json[Permission.required.value] 253 254 permissions = enum.Permissions() 255 256 # Define a list of permission attributes to check 257 permission_keys = [ 258 "screen_capture", 259 "webrtc", 260 "clipboard_access" 261 ] 262 263 # Assign values for optional permissions 264 for key in permission_keys: 265 if key in optional: 266 setattr(permissions.Optional, key, optional[key]) 267 if key in required: 268 setattr(permissions.Required, key, required[key]) 269 270 return permissions
This function returns the Permissions defined by the server. https://docs.altv.mp/articles/permissions.html
Returns: None: When an error occurred. But exceptions will still be logged! Permissions: The permissions of the server.
Raises: FetchError: there was an error while getting the data
272 def get_dtc_url(self, password=None) -> str | None: 273 """This function gets the direct connect protocol url of an alt:V Server. 274 (https://docs.altv.mp/articles/connectprotocol.html) 275 276 Args: 277 password (str): The password of the server. 278 279 Returns: 280 None: When an error occurred. But exceptions will still be logged! 281 str: The direct connect protocol url. 282 """ 283 with StringIO() as dtc_url: 284 if self.useCdn: 285 tmp_url = quote(self.cdnUrl, safe='') 286 if "http" not in self.cdnUrl: 287 dtc_url.write(f"altv://connect/http://{tmp_url}") 288 else: 289 dtc_url.write(f"altv://connect/{tmp_url}") 290 else: 291 dtc_url.write(f"altv://connect/{quote(self.address, safe='')}") 292 293 if self.passworded and password is None: 294 logger.warning( 295 "Your server is password protected but you did not supply a password for the Direct Connect Url." 296 ) 297 if password is not None: 298 dtc_url.write(f"?password={quote(password, safe='')}") 299 300 return dtc_url.getvalue()
This function gets the direct connect protocol url of an alt:V Server. (https://docs.altv.mp/articles/connectprotocol.html)
Args: password (str): The password of the server.
Returns: None: When an error occurred. But exceptions will still be logged! str: The direct connect protocol url.
303def get_server_stats() -> dict | None: 304 """Statistics - Player Count across all servers & The amount of servers online 305 306 Returns: 307 None: When an error occurs 308 dict: The stats 309 310 Raises: 311 FetchError: there was an error while getting the data 312 """ 313 try: 314 tmp_data = request(enum.MasterlistUrls.all_server_stats.value) 315 return tmp_data 316 except error.FetchError as e: 317 logger.error(f"error while getting server stats: {e}") 318 raise error.FetchError(f"error while getting server stats: {e}")
Statistics - Player Count across all servers & The amount of servers online
Returns: None: When an error occurs dict: The stats
Raises: FetchError: there was an error while getting the data
321def get_servers() -> list[Server] | None: 322 """Generates a list of all servers that are currently online. 323 Note that the server objects returned are not complete! 324 325 Returns: 326 None: When an error occurs 327 list: List object that contains all servers. 328 329 Raises: 330 FetchError: there was an error while getting the data 331 """ 332 return_servers = [] 333 try: 334 servers = request(enum.MasterlistUrls.all_servers.value) 335 except error.FetchError as e: 336 raise error.FetchError(f"failed to get servers: {e}") 337 338 server_attributes = [ 339 "playersCount", "maxPlayersCount", "passworded", "language", 340 "useEarlyAuth", "earlyAuthUrl", "useCdn", "cdnUrl", "useVoiceChat", 341 "version", "branch", "available", "banned", "name", "publicId", 342 "vanityUrl", "website", "gameMode", "description", "tags", 343 "lastTimeUpdate", "verified", "promoted", "visible", "hasCustomSkin", 344 "bannerUrl", "address", "group", "masterlist_icon_url", 345 "masterlist_banner_url" 346 ] 347 348 for server in servers: 349 tmp_server = Server(server["publicId"], no_fetch=True) 350 for attr in server_attributes: 351 setattr(tmp_server, attr, server[attr]) 352 return_servers.append(tmp_server) 353 354 return return_servers
Generates a list of all servers that are currently online. Note that the server objects returned are not complete!
Returns: None: When an error occurs list: List object that contains all servers.
Raises: FetchError: there was an error while getting the data
357def validate_id(server_id: any) -> bool: 358 """Validate a server id 359 360 Args: 361 server_id (any): The id you want to check. 362 363 Returns: 364 bool: True = valid, False = invalid 365 """ 366 regex = compile(r"^[\da-zA-Z]{7}$") 367 return isinstance(server_id, str) and regex.match(server_id) is not None
Validate a server id
Args: server_id (any): The id you want to check.
Returns: bool: True = valid, False = invalid