client.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. """
  2. Client module
  3. """
  4. import sys
  5. import logging
  6. import re
  7. import time
  8. from datetime import datetime
  9. import json
  10. import requests
  11. import cfscrape
  12. from webbot.webbot import Browser
  13. # get logger
  14. LOGGER = logging.getLogger(__name__)
  15. LOGGER.setLevel(logging.DEBUG)
  16. # create file handler
  17. FILE_HANDLER = logging.FileHandler('output.log')
  18. FILE_HANDLER.setLevel(logging.DEBUG)
  19. # create console handler
  20. STREAM_HANDLER = logging.StreamHandler()
  21. STREAM_HANDLER.setLevel(logging.INFO)
  22. # create formatter and add it to the handlers
  23. STREAM_FORMATTER = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
  24. STREAM_HANDLER.setFormatter(STREAM_FORMATTER)
  25. FILE_FORMATTER = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  26. FILE_HANDLER.setFormatter(FILE_FORMATTER)
  27. # add the handlers to logger
  28. LOGGER.addHandler(STREAM_HANDLER)
  29. LOGGER.addHandler(FILE_HANDLER)
  30. class RRClientException(Exception):
  31. """RR exception"""
  32. def __init__(self, *args, **kwargs):
  33. Exception.__init__(self, *args, **kwargs)
  34. LOGGER.warning('RRClientException')
  35. class SessionExpireException(Exception):
  36. """Raise when session has expired"""
  37. def __init__(self, *args, **kwargs):
  38. Exception.__init__(self, *args, **kwargs)
  39. LOGGER.warning('Session has expired')
  40. class NoLogginException(Exception):
  41. """Raise exception when client isn't logged in"""
  42. def __init__(self, *args, **kwargs):
  43. Exception.__init__(self, *args, **kwargs)
  44. LOGGER.warning('Session has expired')
  45. class NoPHPsessidException(Exception):
  46. """Raise exception when cookie isn't found"""
  47. def __init__(self, *args, **kwargs):
  48. Exception.__init__(self, *args, **kwargs)
  49. LOGGER.warning('No phpsessid found')
  50. def session_handler(func):
  51. """Handle expired sessions"""
  52. def wrapper(*args, **kwargs):
  53. instance = args[0]
  54. return try_run(instance, func, *args, **kwargs)
  55. def try_run(instance, func, *args, **kwargs):
  56. try:
  57. return func(*args, **kwargs)
  58. except (SessionExpireException, ConnectionError, ConnectionResetError):
  59. instance.remove_cookie(instance.username)
  60. instance.login()
  61. return try_run(instance, func, *args, **kwargs)
  62. except NoLogginException:
  63. instance.login()
  64. return try_run(instance, func, *args, **kwargs)
  65. return wrapper
  66. class Client:
  67. """class for RR client"""
  68. cookie = None
  69. var_c = None
  70. login_method = None
  71. username = None
  72. password = None
  73. session = None
  74. def __init__(self, show_window=False):
  75. self.show_window = show_window
  76. LOGGER.info('Init client, show window %s', self.show_window)
  77. def set_credentials(self, credentials):
  78. """Set the credentials"""
  79. LOGGER.info('Setting "%s" credentials', credentials['username'])
  80. self.login_method = credentials['login_method']
  81. self.username = credentials['username']
  82. self.password = credentials['password']
  83. def login(self):
  84. """Login user if needed"""
  85. cookie = self.get_cookie(self.username)
  86. if cookie is None:
  87. LOGGER.info('Client login "%s" username "%s"', self.login_method, self.username)
  88. if self.login_method not in ["g", "google", "v", "vk", "f", "facebook"]:
  89. raise RRClientException("Not a valid login method.")
  90. auth_text = requests.get("https://rivalregions.com").text
  91. web = Browser(showWindow=self.show_window)
  92. method_dict = {
  93. 'g': self.login_google,
  94. 'google': self.login_google,
  95. 'v': self.login_vk,
  96. 'vk': self.login_vk,
  97. 'f': self.login_facebook,
  98. 'facebook': self.login_facebook,
  99. }
  100. if self.login_method in method_dict:
  101. web = method_dict[self.login_method](web, auth_text)
  102. else:
  103. LOGGER.info('Invallid loggin method "%s"', self.login_method)
  104. sys.exit()
  105. LOGGER.debug('Get cookie')
  106. phpsessid = web.get_cookie('PHPSESSID')
  107. if phpsessid:
  108. cookie = self.create_cookie(
  109. phpsessid.get('expiry', None),
  110. phpsessid.get('value', None)
  111. )
  112. self.write_cookie(self.username, cookie)
  113. else:
  114. raise NoPHPsessidException()
  115. LOGGER.debug('closing login tab')
  116. web.close_current_tab()
  117. self.session = cfscrape.CloudflareScraper()
  118. self.cookie = cookie
  119. self.session.cookies.set(**cookie)
  120. LOGGER.debug('set the var_c')
  121. response = self.session.get('https://rivalregions.com/#overview')
  122. lines = response.text.split("\n")
  123. for line in lines:
  124. if re.match("(.*)var c_html(.*)", line):
  125. var_c = line.split("'")[-2]
  126. LOGGER.debug('var_c: %s', var_c)
  127. self.var_c = line.split("'")[-2]
  128. # This is working
  129. def login_google(self, web, auth_text):
  130. """login using Google"""
  131. LOGGER.info('Login method Google')
  132. auth_text1 = auth_text.split('\t<a href="')
  133. auth_text2 = auth_text1[1].split('" class="sa')
  134. web.go_to(auth_text2[0])
  135. LOGGER.info('Typing in username')
  136. web.type(self.username, into='Email')
  137. web.click('Volgende')
  138. time.sleep(2)
  139. LOGGER.info('Typing in password')
  140. web.type(self.password, css_selector="input")
  141. if web.exists('Sign in'): # English
  142. web.click('Sign in')
  143. elif web.exists('Inloggen'): # Dutch
  144. web.click('Inloggen')
  145. web.click(css_selector=".sa_sn.float_left.imp.gogo")
  146. time.sleep(1)
  147. return web
  148. # IDK if this is working
  149. def login_vk(self, web, auth_text):
  150. """login using VK"""
  151. LOGGER.info('Login method VK')
  152. auth_text1 = auth_text.split("(\'.vkvk\').attr(\'url\', \'")
  153. auth_text2 = auth_text1[1].split('&response')
  154. web.go_to(auth_text2[0])
  155. web.type(self.username, into='email')
  156. web.type(self.password, xpath="/html/body/div/div/div/div[2]/form/div/div/input[7]")
  157. web.click('Log in')
  158. return web
  159. # IDK if this is working
  160. def login_facebook(self, web, auth_text):
  161. """login using Facebook"""
  162. LOGGER.info('Login method Facebook')
  163. auth_text1 = auth_text.split('">\r\n\t\t\t\t<div class="sa_sn imp float_left" ')
  164. auth_text2 = auth_text1[0].split('200px;"><a class="sa_link" href="')
  165. url = auth_text2[1]
  166. web.go_to(url)
  167. web.type(self.username, into='Email')
  168. web.type(self.password, into='Password')
  169. web.click('Log In')
  170. time.sleep(5)
  171. web.click(css_selector='.sa_sn.imp.float_left')
  172. return web
  173. @classmethod
  174. def write_cookie(cls, username, cookie):
  175. """Write cookie to file"""
  176. LOGGER.info('Saving cookie for "%s"', username)
  177. cookies = None
  178. try:
  179. with open('cookies.json', 'r') as cookies_file:
  180. cookies = json.load(cookies_file)
  181. except FileNotFoundError:
  182. cookies = {}
  183. cookies[username] = {
  184. 'expires': cookie['expires'],
  185. 'value': cookie['value'],
  186. }
  187. with open('cookies.json', 'w+') as cookies_file:
  188. json.dump(cookies, cookies_file)
  189. LOGGER.info('Saved cookie for "%s"', username)
  190. @classmethod
  191. def get_cookie(cls, username):
  192. """Read cookies for username"""
  193. LOGGER.info('Read cookie for "%s"', username)
  194. try:
  195. with open('cookies.json', 'r') as cookies_file:
  196. cookies = json.load(cookies_file)
  197. for cookie_username, cookie in cookies.items():
  198. if cookie_username == username:
  199. LOGGER.info('Found cookie')
  200. expires = datetime.fromtimestamp(int(cookie['expires']))
  201. if datetime.now() >= expires:
  202. LOGGER.info('Cookie is expired')
  203. return None
  204. cookie = cls.create_cookie(
  205. cookie['expires'],
  206. cookie['value'],
  207. )
  208. return cookie
  209. except FileNotFoundError:
  210. pass
  211. return None
  212. @classmethod
  213. def remove_cookie(cls, username):
  214. """Remove cookie from storage"""
  215. LOGGER.info('Removing cookie for "%s"', username)
  216. cookies = None
  217. try:
  218. with open('cookies.json', 'r') as cookies_file:
  219. cookies = json.load(cookies_file)
  220. except FileNotFoundError:
  221. cookies = {}
  222. cookies.pop(username, None)
  223. with open('cookies.json', 'w+') as cookies_file:
  224. json.dump(cookies, cookies_file)
  225. LOGGER.info('Removed cookie for "%s"', username)
  226. @staticmethod
  227. def create_cookie(expires, value):
  228. """Create cookie"""
  229. return {
  230. 'domain': 'rivalregions.com',
  231. 'name': 'PHPSESSID',
  232. 'path': '/',
  233. 'secure': False,
  234. 'expires': expires,
  235. 'value': value,
  236. }
  237. @session_handler
  238. def get(self, path, add_var_c=False):
  239. """Send get request to Rival Regions"""
  240. if path[0] == '/':
  241. path = path[1:]
  242. params = {}
  243. if add_var_c:
  244. params['c'] = self.var_c
  245. LOGGER.debug('GET: %s var_c: %s', path, add_var_c)
  246. if self.session:
  247. response = self.session.get(
  248. url='https://rivalregions.com/{}'.format(path),
  249. params=params
  250. )
  251. if "Session expired, please, reload the page" in response.text or \
  252. 'window.location="https://rivalregions.com";' in response.text:
  253. raise SessionExpireException()
  254. else:
  255. raise NoLogginException()
  256. return response.text
  257. @session_handler
  258. def post(self, path, data=None):
  259. """Send post request to Rival Regions"""
  260. if path[0] == '/':
  261. path = path[1:]
  262. data['c'] = self.var_c
  263. LOGGER.debug('POST: %s', path)
  264. if self.session:
  265. response = self.session.post(
  266. "https://rivalregions.com/{}".format(path),
  267. data=data
  268. )
  269. if "Session expired, please, reload the page" in response.text or \
  270. 'window.location="https://rivalregions.com";' in response.text:
  271. raise SessionExpireException()
  272. else:
  273. raise NoLogginException()
  274. return response.text
  275. @session_handler
  276. def send_chat(self, language, message):
  277. """send chat message"""
  278. LOGGER.info('language %s: start sending message', language)
  279. if self.session:
  280. response = self.session.get("https://rivalregions.com/#overview")
  281. if "Session expired, please, reload the page" in response.text:
  282. raise SessionExpireException()
  283. web = Browser(showWindow=self.show_window)
  284. web.go_to('https://rivalregions.com/')
  285. web.add_cookie(self.get_cookie(self.username))
  286. web.go_to('https://rivalregions.com/#slide/chat/lang_{}'.format(language))
  287. web.refresh()
  288. time.sleep(2)
  289. web.type(message, id='message')
  290. web.click(id='chat_send')
  291. LOGGER.info('language %s: finished sending message', language)
  292. web.close_current_tab()
  293. else:
  294. raise NoLogginException()
  295. @session_handler
  296. def send_personal_message(self, user_id, message):
  297. """send personal message"""
  298. LOGGER.info('user %s: start sending message', user_id)
  299. if self.session:
  300. response = self.session.get("https://rivalregions.com/#overview")
  301. if "Session expired, please, reload the page" in response.text:
  302. raise SessionExpireException()
  303. web = Browser(showWindow=self.show_window)
  304. web.go_to('https://rivalregions.com/')
  305. web.add_cookie(self.get_cookie(self.username))
  306. web.go_to('https://rivalregions.com/#messages/{}'.format(user_id))
  307. web.refresh()
  308. time.sleep(2)
  309. web.type(message, id='message')
  310. web.click(id='chat_send')
  311. LOGGER.info('user %s: finished sending message', user_id)
  312. web.close_current_tab()
  313. else:
  314. raise NoLogginException()
  315. @session_handler
  316. def send_conference_message(self, conference_id, message):
  317. """send conference message"""
  318. LOGGER.info('conference %s: start sending message', conference_id)
  319. if self.session:
  320. response = self.session.get("https://rivalregions.com/#overview")
  321. if "Session expired, please, reload the page" in response.text:
  322. raise SessionExpireException()
  323. web = Browser(showWindow=self.show_window)
  324. web.go_to('https://rivalregions.com/')
  325. web.add_cookie(self.get_cookie(self.username))
  326. web.go_to('https://rivalregions.com/#slide/conference/{}'.format(conference_id))
  327. web.refresh()
  328. time.sleep(2)
  329. character_count = 0
  330. tmp_messages = []
  331. for sentence in message.split('\n'):
  332. sentence_character_count = 0
  333. tmp_sentence = []
  334. for word in sentence.split(' '):
  335. sentence_character_count += len(word) + 1
  336. if sentence_character_count >= 899:
  337. message = '{}\n{}'.format('\n'.join(tmp_messages), ' '.join(tmp_sentence))
  338. LOGGER.info(
  339. 'conference %s: next message length: %s', conference_id, len(message)
  340. )
  341. web.type(message, id='message')
  342. web.click(id='chat_send')
  343. sentence_character_count = 0
  344. tmp_sentence = []
  345. character_count = 0
  346. tmp_messages = []
  347. tmp_sentence.append(word)
  348. sentence = ' '.join(tmp_sentence)
  349. character_count += len(sentence) + 1
  350. if character_count >= 900:
  351. message = '\n'.join(tmp_messages)
  352. LOGGER.info(
  353. 'conference %s: next message length: %s', conference_id, len(message)
  354. )
  355. web.type(message, id='message')
  356. web.click(id='chat_send')
  357. character_count = 0
  358. tmp_messages = []
  359. tmp_messages.append(sentence)
  360. if tmp_messages:
  361. message = '\n'.join(tmp_messages)
  362. LOGGER.info(
  363. 'conference %s: next message length: %s', conference_id, len(message)
  364. )
  365. web.type(message, id='message')
  366. web.click(id='chat_send')
  367. LOGGER.info('conference %s: finished sending message', conference_id)
  368. web.close_current_tab()
  369. else:
  370. raise NoLogginException()
  371. @session_handler
  372. def send_conference_notification(self, conference_id, message, sound):
  373. """send conference notification"""
  374. LOGGER.info('conference %s: start sending notification', conference_id)
  375. data = {
  376. 'sound': 1 if sound else 0,
  377. 'text': message,
  378. 'c': self.var_c
  379. }
  380. if self.session:
  381. LOGGER.info('conference %s: sending notification', conference_id)
  382. response = self.session.post(
  383. "https://rivalregions.com/rival/konffcm/{}/".format(conference_id),
  384. data=data
  385. )
  386. if "Session expired, please, reload the page" in response.text or \
  387. 'window.location="https://rivalregions.com";' in response.text:
  388. raise SessionExpireException()
  389. else:
  390. raise NoLogginException()
  391. return response.text