instances.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. ########################################################################
  2. # searxpy - Provides Python modules for interacting with searx and
  3. # searx-stats2 instances.
  4. # Copyright (C) 2020 CYBERDEViL
  5. #
  6. # This file is part of searxpy.
  7. #
  8. # searxpy is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # searxpy is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #
  21. ########################################################################
  22. import requests
  23. import json
  24. class SearchEngine:
  25. def __init__(self, name, data):
  26. """Model for a search engine data.
  27. @param name: Name of the search engine
  28. @type name: str
  29. @param data: Data of the search engine
  30. @type data: dict
  31. """
  32. self._name = name
  33. self._data = data
  34. def __repr__(self): return self._name
  35. @property
  36. def name(self):
  37. """
  38. @return: returns the name of this search engine.
  39. @rtype: str
  40. """
  41. return self._name
  42. @property
  43. def enabled(self):
  44. """If this instance has this search engine enabled or not.
  45. @return:
  46. @rtype: bool
  47. """
  48. return self._data.get('enabled', False)
  49. @property
  50. def stats(self):
  51. """
  52. @return:
  53. @rtype: bool
  54. """
  55. return self._data.get('stats', False)
  56. class TLS:
  57. def __init__(self, data):
  58. """Model for a instance it's TLS data.
  59. @param data: dict with the instance it's TLS data.
  60. @type data: dict
  61. """
  62. self._data = data
  63. @property
  64. def data(self): return self._data
  65. @property
  66. def version(self):
  67. """Returns the TLS version used.
  68. @return:
  69. @rtype: str
  70. """
  71. return self.data.get('version', "")
  72. @property
  73. def certificate(self):
  74. """Returns a TLSCertificate object.
  75. @return:
  76. @rtype: TLSCertificate
  77. """
  78. return TLSCertificate(self.data.get('certificate', {}))
  79. class TLSCertificate:
  80. def __init__(self, data):
  81. """Model for a instance it's TLS certificate-data.
  82. @param data: dict with the instance it's TLS certificate-data.
  83. @type data: dict
  84. """
  85. self._data = data
  86. @property
  87. def data(self): return self._data
  88. @property
  89. def version(self):
  90. """
  91. @return:
  92. @rtype: int
  93. """
  94. return self.data.get('version', 0)
  95. @property
  96. def issuer(self):
  97. """
  98. @return:
  99. @rtype: TLSCertificateIssuer
  100. """
  101. return TLSCertificateIssuer(self.data.get('issuer', {}))
  102. class TLSCertificateIssuer:
  103. def __init__(self, data):
  104. """Model for a instance it's TLS certificate-issuer-data.
  105. @param data: dict with the instance it's
  106. TLS certificate-issuer-data.
  107. @type data: dict
  108. """
  109. self._data = data
  110. @property
  111. def data(self): return self._data
  112. @property
  113. def commonName(self):
  114. """
  115. @rtype: str
  116. """
  117. return self.data.get('commonName', "")
  118. @property
  119. def countryName(self):
  120. """
  121. @rtype: str
  122. """
  123. return self.data.get('countryName', "")
  124. @property
  125. def organizationName(self):
  126. """
  127. @rtype: str
  128. """
  129. return self.data.get('organizationName', "")
  130. class Network:
  131. def __init__(self, data):
  132. """Model for a instance it's network data.
  133. @param data: dict with the instance it's network data.
  134. @type data: dict
  135. """
  136. self._data = data
  137. @property
  138. def data(self): return self._data
  139. @property
  140. def ipv6(self):
  141. """
  142. @return: If ipv6 is enabled on this instance.
  143. @rtype: bool
  144. """
  145. return self.data.get('ipv6', False)
  146. @property
  147. def ips(self):
  148. """
  149. @return: A list with NetworkIP objects
  150. @rtype: list
  151. """
  152. return [NetworkIP(ip, data)
  153. for ip, data in self.data.get('ips', {}).items()]
  154. @property
  155. def asnPrivacy(self):
  156. """
  157. @return: -1 no asn privacy, 0 asn privacy, -2 unknown.
  158. @rtype: int
  159. """
  160. return self.data.get('asn_privacy', -2)
  161. class NetworkIP:
  162. def __init__(self, ip, data):
  163. """Model for a network ip data.
  164. @param ip: ip address
  165. @type ip: str
  166. @param data: dict with a network ip data.
  167. @type data: dict
  168. """
  169. self._ip = ip
  170. self._data = data
  171. def __repr__(self): return self.ip
  172. def __str__(self): return repr(self)
  173. @property
  174. def ip(self):
  175. """
  176. @return: ip address
  177. @rtype: str
  178. """
  179. return self._ip
  180. @property
  181. def data(self): return self._data
  182. @property
  183. def reverse(self):
  184. """
  185. @return:
  186. @rtype: str
  187. """
  188. return self.data.get('reverse', None)
  189. @property
  190. def fieldType(self):
  191. """
  192. @return: Record type
  193. @rtype: str
  194. """
  195. return self.data.get('field_type', None)
  196. @property
  197. def asnCidr(self):
  198. """
  199. @return:
  200. @rtype: str
  201. """
  202. return self.data.get('asn_cidr', None)
  203. @property
  204. def httpsPort(self):
  205. """
  206. @return:
  207. @rtype: bool
  208. """
  209. return self.data.get('https_port', None)
  210. class Instance:
  211. def __init__(self, url, data):
  212. """Model for a SearX instance.
  213. @param url: Url of the instance
  214. @type url: str
  215. @param data: Data of the instance
  216. @type data: dict
  217. """
  218. self._url = url
  219. self._data = data
  220. def __str__(self): return self.url
  221. def __repr__(self): return str(self)
  222. @property
  223. def data(self): return self._data
  224. @property
  225. def url(self):
  226. """
  227. @return: returns the url of this instance.
  228. @rtype: str
  229. """
  230. return self._url
  231. @property
  232. def main(self):
  233. """Returns False when not set.
  234. @return: ?
  235. @rtype: bool
  236. """
  237. return self._data.get('main', False)
  238. @property
  239. def networkType(self):
  240. """Returns a empty string when none found.
  241. Note: http, https and i2p return 'normal'; tor returns 'tor'.
  242. @return: Network protocol used (normal or tor)
  243. @rtype: str
  244. """
  245. return self._data.get('network_type', '')
  246. @property
  247. def version(self):
  248. """Returns a empty string when none found.
  249. @return: Returns the instance it's version.
  250. @rtype: str
  251. """
  252. return self._data.get('version', '')
  253. @property
  254. def engines(self):
  255. """
  256. Returns a empty string when none found.
  257. @return: Returns a list with SearchEngine objects
  258. @rtype: list
  259. """
  260. return [SearchEngine(name, data) for name, data in self._data.get(
  261. 'engines', {}).items()]
  262. @property
  263. def tls(self):
  264. """
  265. @rtype: TLS
  266. """
  267. return TLS(self._data.get('tls', {}))
  268. @property
  269. def network(self):
  270. """
  271. @rtype: TLS
  272. """
  273. return Network(self._data.get('network', {}))
  274. class Instances:
  275. URL = "https://searx.space/"
  276. def __init__(self):
  277. self._instances = {}
  278. def __contains__(self, url):
  279. return bool(url in self._instances)
  280. def __iter__(self): return iter(self._instances)
  281. def __getitem__(self, url): return self._instances[url]
  282. def __str__(self): return str([url for url in self])
  283. def __repr__(self): return str(self)
  284. def __len__(self): return len(self._instances)
  285. def items(self): return self._instances.items()
  286. def keys(self): return self._instances.keys()
  287. def values(self): return self._instances.values()
  288. def copy(self): return self._instances.copy()
  289. def data(self):
  290. """
  291. @return: dict with as key the instance it's URL and as value
  292. the instance it's data.
  293. @rtype data: dict
  294. """
  295. r = {}
  296. for url, instance in self._instances.items():
  297. r.update({url: instance.data})
  298. return r
  299. def setData(self, data):
  300. """
  301. @type data: dict
  302. """
  303. self.clear()
  304. for url, instanceData in data.items():
  305. self._instances.update({url: Instance(url, instanceData)})
  306. def clear(self):
  307. """ Clear the instances.
  308. """
  309. self._instances.clear()
  310. def update(self, requestKwargs={}):
  311. """Fetches list with instances from a searx-stats2 instance.
  312. @param requestKwargs: Kwargs to pass to request.get
  313. @type requestKwargs: dict
  314. @return: A tuple (bool Success/Failed, str Message)
  315. @rtype: tuple
  316. """
  317. url = Instances.URL.rstrip('/') + '/data/instances.json'
  318. try:
  319. response = requests.get(url, **requestKwargs)
  320. except requests.exceptions.HTTPError as err:
  321. return (False, err)
  322. except requests.exceptions.ConnectionError as err:
  323. return (False, err)
  324. except requests.exceptions.Timeout as err:
  325. return (False, err)
  326. if response.status_code == 200:
  327. try:
  328. jsonObj = json.loads(response.content)
  329. except json.JSONDecodeError as err:
  330. return (False, err)
  331. else:
  332. self.setData(jsonObj.get('instances', {}))
  333. return (True, "Success.")
  334. return (False, "Wrong response status code: `{0}`."
  335. .format(response.status_code))