Advertisement
Guest User

Untitled

a guest
Jul 20th, 2021
202
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.88 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. # Description: spigotmc netdata python.d module
  3. # Author: Austin S. Hemmelgarn (Ferroin)
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5.  
  6. import platform
  7. import re
  8. import socket
  9. import datetime
  10. import json
  11. import random
  12.  
  13. from typing import List, Optional
  14. from six import string_types
  15.  
  16. from mcstatus.protocol.connection import Connection
  17.  
  18. # Update only every 5 seconds because collection takes in excess of
  19. # 100ms sometimes, and most people won't care about second-by-second data.
  20. update_every = 5
  21.  
  22. PRECISION = 100
  23.  
  24. ORDER = [
  25. 'players',
  26. ]
  27.  
  28. CHARTS = {
  29. 'players': {
  30. 'options': [None, 'Minecraft Users', 'users', 'spigotmc', 'spigotmc.users', 'area'],
  31. 'lines': [
  32. ['players', 'Users', 'absolute', 1, 1]
  33. ]
  34. }
  35. }
  36.  
  37.  
  38.  
  39. COLOR_MAP = {
  40. "dark_red": "4",
  41. "red": "c",
  42. "gold": "6",
  43. "yellow": "e",
  44. "dark_green": "2",
  45. "green": "a",
  46. "aqua": "b",
  47. "dark_aqua": "3",
  48. "dark_blue": "1",
  49. "blue": "9",
  50. "light_purple": "d",
  51. "dark_purple": "5",
  52. "white": "f",
  53. "gray": "7",
  54. "dark_gray": "8",
  55. "black": "0",
  56. }
  57.  
  58.  
  59. class ServerPinger:
  60. def __init__(
  61. self,
  62. connection,
  63. host: str = "IP-ADRESS CENSORED",
  64. port: int = 25565,
  65. version: int = 47,
  66. ping_token=None,
  67. ):
  68. if ping_token is None:
  69. ping_token = random.randint(0, (1 << 63) - 1)
  70. self.version = version
  71. self.connection = connection
  72. self.host = host
  73. self.port = port
  74. self.ping_token = ping_token
  75.  
  76. def handshake(self):
  77. packet = Connection()
  78. packet.write_varint(0)
  79. packet.write_varint(self.version)
  80. packet.write_utf(self.host)
  81. packet.write_ushort(self.port)
  82. packet.write_varint(1) # Intention to query status
  83.  
  84. self.connection.write_buffer(packet)
  85.  
  86. def read_status(self):
  87. request = Connection()
  88. request.write_varint(0) # Request status
  89. self.connection.write_buffer(request)
  90.  
  91. response = self.connection.read_buffer()
  92. if response.read_varint() != 0:
  93. raise IOError("Received invalid status response packet.")
  94. try:
  95. raw = json.loads(response.read_utf())
  96. except ValueError:
  97. raise IOError("Received invalid JSON")
  98. try:
  99. return PingResponse(raw)
  100. except ValueError as e:
  101. raise IOError("Received invalid status response: %s" % e)
  102.  
  103. def test_ping(self):
  104. request = Connection()
  105. request.write_varint(1) # Test ping
  106. request.write_long(self.ping_token)
  107. sent = datetime.datetime.now()
  108. self.connection.write_buffer(request)
  109.  
  110. response = self.connection.read_buffer()
  111. received = datetime.datetime.now()
  112. if response.read_varint() != 1:
  113. raise IOError("Received invalid ping response packet.")
  114. received_token = response.read_long()
  115. if received_token != self.ping_token:
  116. raise IOError(
  117. "Received mangled ping response packet (expected token %d, received %d)" % (self.ping_token, received_token)
  118. )
  119.  
  120. delta = received - sent
  121. # We have no trivial way of getting a time delta :(
  122. return (delta.days * 24 * 60 * 60 + delta.seconds) * 1000 + delta.microseconds / 1000.0
  123.  
  124.  
  125. class AsyncServerPinger(ServerPinger):
  126. async def read_status(self):
  127. request = Connection()
  128. request.write_varint(0) # Request status
  129. self.connection.write_buffer(request)
  130.  
  131. response = await self.connection.read_buffer()
  132. if response.read_varint() != 0:
  133. raise IOError("Received invalid status response packet.")
  134. try:
  135. raw = json.loads(response.read_utf())
  136. except ValueError:
  137. raise IOError("Received invalid JSON")
  138. try:
  139. return PingResponse(raw)
  140. except ValueError as e:
  141. raise IOError("Received invalid status response: %s" % e)
  142.  
  143. async def test_ping(self):
  144. request = Connection()
  145. request.write_varint(1) # Test ping
  146. request.write_long(self.ping_token)
  147. sent = datetime.datetime.now()
  148. self.connection.write_buffer(request)
  149.  
  150. response = await self.connection.read_buffer()
  151. received = datetime.datetime.now()
  152. if response.read_varint() != 1:
  153. raise IOError("Received invalid ping response packet.")
  154. received_token = response.read_long()
  155. if received_token != self.ping_token:
  156. raise IOError(
  157. "Received mangled ping response packet (expected token %d, received %d)" % (self.ping_token, received_token)
  158. )
  159.  
  160. delta = received - sent
  161. # We have no trivial way of getting a time delta :(
  162. return (delta.days * 24 * 60 * 60 + delta.seconds) * 1000 + delta.microseconds / 1000.0
  163.  
  164.  
  165. class PingResponse:
  166. # THIS IS SO UNPYTHONIC
  167. # it's staying just because the tests depend on this structure
  168. class Players:
  169. class Player:
  170. name: str
  171. id: str
  172.  
  173. def __init__(self, raw):
  174. if not isinstance(raw, dict):
  175. raise ValueError("Invalid player object (expected dict, found %s" % type(raw))
  176.  
  177. if "name" not in raw:
  178. raise ValueError("Invalid player object (no 'name' value)")
  179. if not isinstance(raw["name"], string_types):
  180. raise ValueError("Invalid player object (expected 'name' to be str, was %s)" % type(raw["name"]))
  181. self.name = raw["name"]
  182.  
  183. if "id" not in raw:
  184. raise ValueError("Invalid player object (no 'id' value)")
  185. if not isinstance(raw["id"], string_types):
  186. raise ValueError("Invalid player object (expected 'id' to be str, was %s)" % type(raw["id"]))
  187. self.id = raw["id"]
  188.  
  189. online: int
  190. data['players'] = players #######HEREEE
  191. max: int
  192. sample: Optional[List["PingResponse.Players.Player"]]
  193.  
  194. def __init__(self, raw):
  195. if not isinstance(raw, dict):
  196. raise ValueError("Invalid players object (expected dict, found %s" % type(raw))
  197.  
  198. if "online" not in raw:
  199. raise ValueError("Invalid players object (no 'online' value)")
  200. if not isinstance(raw["online"], int):
  201. raise ValueError("Invalid players object (expected 'online' to be int, was %s)" % type(raw["online"]))
  202. self.online = raw["online"]
  203.  
  204. if "max" not in raw:
  205. raise ValueError("Invalid players object (no 'max' value)")
  206. if not isinstance(raw["max"], int):
  207. raise ValueError("Invalid players object (expected 'max' to be int, was %s)" % type(raw["max"]))
  208. self.max = raw["max"]
  209.  
  210. if "sample" in raw:
  211. if not isinstance(raw["sample"], list):
  212. raise ValueError("Invalid players object (expected 'sample' to be list, was %s)" % type(raw["max"]))
  213. self.sample = [PingResponse.Players.Player(p) for p in raw["sample"]]
  214. else:
  215. self.sample = None
  216.  
  217. class Version:
  218. name: str
  219. protocol: int
  220.  
  221. def __init__(self, raw):
  222. if not isinstance(raw, dict):
  223. raise ValueError("Invalid version object (expected dict, found %s" % type(raw))
  224.  
  225. if "name" not in raw:
  226. raise ValueError("Invalid version object (no 'name' value)")
  227. if not isinstance(raw["name"], string_types):
  228. raise ValueError("Invalid version object (expected 'name' to be str, was %s)" % type(raw["name"]))
  229. self.name = raw["name"]
  230.  
  231. if "protocol" not in raw:
  232. raise ValueError("Invalid version object (no 'protocol' value)")
  233. if not isinstance(raw["protocol"], int):
  234. raise ValueError("Invalid version object (expected 'protocol' to be int, was %s)" % type(raw["protocol"]))
  235. self.protocol = raw["protocol"]
  236.  
  237. players: Players
  238. version: Version
  239. description: str
  240. favicon: Optional[str]
  241. latency: float = 0
  242.  
  243. def __init__(self, raw):
  244. self.raw = raw
  245.  
  246. if "players" not in raw:
  247. raise ValueError("Invalid status object (no 'players' value)")
  248. self.players = PingResponse.Players(raw["players"])
  249.  
  250. if "version" not in raw:
  251. raise ValueError("Invalid status object (no 'version' value)")
  252. self.version = PingResponse.Version(raw["version"])
  253.  
  254. if "description" not in raw:
  255. raise ValueError("Invalid status object (no 'description' value)")
  256. if isinstance(raw["description"], (dict, list)):
  257. if isinstance(raw["description"], dict):
  258. entries = raw["description"].get("extra", ())
  259. end = raw["description"]["text"]
  260. else:
  261. entries = raw["description"]
  262. end = ""
  263.  
  264. description = ""
  265.  
  266. for entry in entries:
  267. if entry.get("bold"):
  268. description += "§l"
  269.  
  270. if entry.get("italic"):
  271. description += "§o"
  272.  
  273. if entry.get("underlined"):
  274. description += "§n"
  275.  
  276. if entry.get("obfuscated"):
  277. description += "§k"
  278.  
  279. if entry.get("color"):
  280. description += "§" + COLOR_MAP[entry["color"]]
  281.  
  282. description += entry.get("text", "")
  283.  
  284. self.description = description + end
  285. else:
  286. self.description = raw["description"]
  287.  
  288. self.favicon = raw.get("favicon")
  289.  
  290.  
  291. return data
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement