From 7b992afe70925f244ca6e9746071ae71e497b54e Mon Sep 17 00:00:00 2001 From: Dmitrii Morozov Date: Mon, 8 Apr 2024 23:40:24 +0200 Subject: Observe status of friends --- .gitignore | 2 +- DeviceAuth.py | 26 +++++++++++++ FortniteClient.py | 85 +++++++++++++++++++++++++++++++++++++++++++ FortniteEvents.py | 34 +++++++++++++++++ FortniteStatusFormatter.py | 21 +++++++++++ FortniteStatusWrapper.py | 40 ++++++++++++++++++++ fortniteStatusFormatter.py | 21 ----------- fortniteStatusWrapper.py | 39 -------------------- fortniteTest.py | 91 ---------------------------------------------- tgbot.py | 20 ++++++++-- 10 files changed, 224 insertions(+), 155 deletions(-) create mode 100644 DeviceAuth.py create mode 100755 FortniteClient.py create mode 100644 FortniteEvents.py create mode 100644 FortniteStatusFormatter.py create mode 100644 FortniteStatusWrapper.py delete mode 100644 fortniteStatusFormatter.py delete mode 100644 fortniteStatusWrapper.py delete mode 100755 fortniteTest.py diff --git a/.gitignore b/.gitignore index ebad2d3..c1a6602 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ __pycache__/ db.sqlite venv/ -device_auths.json \ No newline at end of file +device-auth.json \ No newline at end of file diff --git a/DeviceAuth.py b/DeviceAuth.py new file mode 100644 index 0000000..d62731c --- /dev/null +++ b/DeviceAuth.py @@ -0,0 +1,26 @@ +import json +import os + +class DeviceAuth: + + __filename__ = 'device-auth.json' + + def __init__(self): + print('DeviceAuth init') + + + def device_auth_file_exists(self): + return os.path.isfile(self.__filename__) + + def get_device_auth_details(self): + if os.path.isfile(self.__filename__): + with open(self.__filename__, 'r') as fp: + return json.load(fp) + return {} + + def store_device_auth_details(self, email, details): + existing = self.get_device_auth_details() + existing[email] = details + + with open(self.__filename__, 'w') as fp: + json.dump(existing, fp) \ No newline at end of file diff --git a/FortniteClient.py b/FortniteClient.py new file mode 100755 index 0000000..f426fa7 --- /dev/null +++ b/FortniteClient.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 + +import fortnitepy +import json +import os +import typing +from DeviceAuth import DeviceAuth +from FortniteEvents import * + +__fortnite_account_key__ = 'fornite-account-key' + +class FortniteClient(fortnitepy.Client): + + device_auth = DeviceAuth() + observers = [] + + def __init__(self): + if self.device_auth.device_auth_file_exists(): + self.__auth_device_auth() + else: + self.__auth_authorization_code() + + def attach(self, observer: any): + self.observers.append(observer) + + def __auth_authorization_code(self): + code = input("Enter authorization code (https://www.epicgames.com/id/api/redirect?clientId=3446cd72694c4a4485d81b77adbb2141&responseType=code):") + super().__init__( + auth=fortnitepy.AuthorizationCodeAuth( + code = code + ) + ) + + def __auth_device_auth(self): + device_auth_details = self.device_auth.get_device_auth_details().get(__fortnite_account_key__, {}) + super().__init__( + auth=fortnitepy.DeviceAuth( + **device_auth_details + ) + ) + + async def event_device_auth_generate(self, details, email): + self.device_auth.store_device_auth_details(email, details) + + # Generate auth details if none were supplied yet + async def generate_auth_details(self): + if not self.device_auth.device_auth_file_exists(): + device_auth_data = await self.auth.generate_device_auth() + details = { + 'device_id': device_auth_data['deviceId'], + 'account_id': device_auth_data['accountId'], + 'secret': device_auth_data['secret'], + } + self.auth.__dict__.update(details) + self.dispatch_event( + 'device_auth_generate', + details, + __fortnite_account_key__ + ) + + async def event_ready(self): + print('----------------') + print('FortniteClient ready as:') + print(self.user.display_name) + print(self.user.id) + print('----------------') + + await self.generate_auth_details() + + # Accept pending friends + for friend_request in self.incoming_pending_friends: + await self.event_friend_request(friend_request) + + async def event_party_invite(self, invitation: fortnitepy.ReceivedPartyInvitation): + await PartyInvite.on_event(invitation = invitation) + + async def event_friend_request(self, request: typing.Union[fortnitepy.friend.IncomingPendingFriend, fortnitepy.friend.OutgoingPendingFriend]): + await IncomingFriendRequest.on_event(request) + + async def event_friend_presence(self, before, after: fortnitepy.Presence): + await FriendPresence.on_event(before, after, self.observers) + +# Debug +#client = FortniteClient() +#client.run() \ No newline at end of file diff --git a/FortniteEvents.py b/FortniteEvents.py new file mode 100644 index 0000000..d8fac0d --- /dev/null +++ b/FortniteEvents.py @@ -0,0 +1,34 @@ +import fortnitepy +import typing + +class PartyInvite: + + async def on_event(invitation: fortnitepy.ReceivedPartyInvitation): + print('Accepting party invitation from {}'.format(invitation.sender.display_name)) + clientParty = await invitation.accept() + for partyMember in clientParty.members: + if not self.get_friend(partyMember.id) and self.user.id != partyMember.id: + print('Adding {} as friend'.format(partyMember.display_name)) + await partyMember.add() + +class IncomingFriendRequest: + + async def on_event(request: typing.Union[fortnitepy.friend.IncomingPendingFriend, fortnitepy.friend.OutgoingPendingFriend]): + if isinstance(request, fortnitepy.friend.IncomingPendingFriend): + incoming_request = typing.cast(fortnitepy.friend.IncomingPendingFriend, request) + print('Accepting friend request from {}'.format(incoming_request.display_name)) + await incoming_request.accept() + +class PresenceObserver: + def update(self, display_name: str, playing: bool) -> None: + pass + +class FriendPresence: + + async def on_event(before, after: fortnitepy.Presence, observers): + if before is not None and after is not None: + if before.playing != after.playing: + print('FriendPresence changed for user {}, before {}, after {}'.format(after.friend.display_name, before.playing, after.playing)) + for observer in observers: + if isinstance(observer, PresenceObserver): + observer.update(after.friend.display_name, after.playing) diff --git a/FortniteStatusFormatter.py b/FortniteStatusFormatter.py new file mode 100644 index 0000000..9595304 --- /dev/null +++ b/FortniteStatusFormatter.py @@ -0,0 +1,21 @@ +from telebot import formatting + +def __formatStatus(status): + if (status == True): + return u'\u2705' + else: + return u'\u274c' + +def __formatFortniteServiceStatus(fortniteServiceStatus): + return formatting.format_text( + formatting.mbold(fortniteServiceStatus.serviceName), + __formatStatus(fortniteServiceStatus.status), + separator=': ') + +def formatFortniteStatus(fortniteStatus): + statuses = [__formatFortniteServiceStatus(serviceStatus) for serviceStatus in fortniteStatus.serviceStatuses] + return formatting.format_text( + formatting.mbold("Fortnite status"), + "", + '\n'.join(statuses), + separator='\n') \ No newline at end of file diff --git a/FortniteStatusWrapper.py b/FortniteStatusWrapper.py new file mode 100644 index 0000000..6a80348 --- /dev/null +++ b/FortniteStatusWrapper.py @@ -0,0 +1,40 @@ +from telebot import formatting +import time, threading, schedule +from pythonFortniteStatus.FortniteStatus import * + +__polling_interval__ = 5 + +fortniteStatus = FortniteStatus() + +class Observer: + def update(self, fortniteStatus) -> None: + pass + +class FortniteStatusWrapper: + + observers = [] + fortniteStatus = None + + def __init__(self): + schedule.every(__polling_interval__).seconds.do(self.__readStatus) + threading.Thread(target=self.__scheduleHandler, name='fortnite_status_scheduler', daemon=True).start() + self.fortniteStatus = fortniteStatus.getStatus() + + def __scheduleHandler(self): + while True: + schedule.run_pending() + time.sleep(1) + + def __readStatus(self): + serviceStatusTmp = fortniteStatus.getStatus() + if serviceStatusTmp != self.fortniteStatus: + self.notify(serviceStatusTmp) + self.fortniteStatus = serviceStatusTmp + + def notify(self, fortniteStatus): + print("Fortnite status changed, notifying observers") + for observer in self.observers: + observer.update(fortniteStatus) + + def attach(self, observer: Observer): + self.observers.append(observer) \ No newline at end of file diff --git a/fortniteStatusFormatter.py b/fortniteStatusFormatter.py deleted file mode 100644 index 9595304..0000000 --- a/fortniteStatusFormatter.py +++ /dev/null @@ -1,21 +0,0 @@ -from telebot import formatting - -def __formatStatus(status): - if (status == True): - return u'\u2705' - else: - return u'\u274c' - -def __formatFortniteServiceStatus(fortniteServiceStatus): - return formatting.format_text( - formatting.mbold(fortniteServiceStatus.serviceName), - __formatStatus(fortniteServiceStatus.status), - separator=': ') - -def formatFortniteStatus(fortniteStatus): - statuses = [__formatFortniteServiceStatus(serviceStatus) for serviceStatus in fortniteStatus.serviceStatuses] - return formatting.format_text( - formatting.mbold("Fortnite status"), - "", - '\n'.join(statuses), - separator='\n') \ No newline at end of file diff --git a/fortniteStatusWrapper.py b/fortniteStatusWrapper.py deleted file mode 100644 index 48618be..0000000 --- a/fortniteStatusWrapper.py +++ /dev/null @@ -1,39 +0,0 @@ -from telebot import formatting -import time, threading, schedule -from pythonFortniteStatus.FortniteStatus import * - -__polling_interval__ = 5 - -fortniteStatus = FortniteStatus() - -class Observer: - def update(self, fortniteStatus) -> None: - pass - -class FortniteStatusWrapper: - - observers = [] - fortniteStatus = None - - def __init__(self): - schedule.every(__polling_interval__).seconds.do(self.__readStatus) - threading.Thread(target=self.__scheduleHandler, name='fortnite_status_scheduler', daemon=True).start() - - def __scheduleHandler(self): - while True: - schedule.run_pending() - time.sleep(1) - - def __readStatus(self): - serviceStatusTmp = fortniteStatus.getStatus() - if serviceStatusTmp != self.fortniteStatus: - self.notify(serviceStatusTmp) - self.fortniteStatus = serviceStatusTmp - - def notify(self, fortniteStatus): - print("Fortnite status changed, notifying observers") - for observer in self.observers: - observer.update(fortniteStatus) - - def attach(self, observer: Observer): - self.observers.append(observer) \ No newline at end of file diff --git a/fortniteTest.py b/fortniteTest.py deleted file mode 100755 index a00283f..0000000 --- a/fortniteTest.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/python3 - -import fortnitepy -import json -import os - -email = 'dmitry-morozov.rjkv73@yandex.ru' -password = 'Dimka2407985$' -filename = 'device_auths.json' - -class MyClient(fortnitepy.Client): - def __init__(self): - if self._device_auth_file_exists(): - device_auth_details = self.get_device_auth_details().get(email, {}) - super().__init__( - auth=fortnitepy.DeviceAuth( - **device_auth_details - ) - ) - else: - code = input("Enter authorization code (https://www.epicgames.com/id/api/redirect?clientId=3446cd72694c4a4485d81b77adbb2141&responseType=code):") - print(code) - super().__init__( - auth=fortnitepy.AuthorizationCodeAuth( - code = code - ) - ) - - def _device_auth_file_exists(self): - return os.path.isfile(filename) - - def get_device_auth_details(self): - if os.path.isfile(filename): - with open(filename, 'r') as fp: - return json.load(fp) - return {} - - def store_device_auth_details(self, email, details): - existing = self.get_device_auth_details() - existing[email] = details - - with open(filename, 'w') as fp: - json.dump(existing, fp) - - async def event_device_auth_generate(self, details, email): - self.store_device_auth_details(email, details) - - async def event_ready(self): - print('----------------') - print('Client ready as') - print(self.user.display_name) - print(self.user.id) - print('----------------') - - if not self._device_auth_file_exists(): - device_auth_data = await self.auth.generate_device_auth() - details = { - 'device_id': device_auth_data['deviceId'], - 'account_id': device_auth_data['accountId'], - 'secret': device_auth_data['secret'], - } - self.auth.__dict__.update(details) - self.dispatch_event( - 'device_auth_generate', - details, - email - ) - - for incomingPedingFriend in self.incoming_pending_friends: - print('Accepting friend request') - await incomingPedingFriend.accept() - - async def event_party_invite(self, invitation: fortnitepy.ReceivedPartyInvitation): - clientParty = await invitation.accept() - for partyMember in clientParty.members: - if not self.get_friend(partyMember.id) and self.user.id != partyMember.id: - print('Adding {} as friend'.format(partyMember.display_name)) - await partyMember.add() - - async def event_friend_request(self, request: fortnitepy.friend.IncomingPendingFriend): - print('Accepting friend request') - await request.accept() - - async def event_friend_presence(self, before, after: fortnitepy.Presence): - if before is not None and after is not None: - print('event_friend_presence for user {}'.format(after.friend.display_name)) - print("Before available {}, joinable {}, playing {}, lfg {}".format(before.available, before.joinable, before.playing, before.lfg)) - print("After available {}, joinable {}, playing {}, lfg {}".format(after.available, after.joinable, after.playing, after.lfg)) - -client = MyClient() -client.run() \ No newline at end of file diff --git a/tgbot.py b/tgbot.py index 99d6351..72e7aa9 100755 --- a/tgbot.py +++ b/tgbot.py @@ -3,8 +3,10 @@ import os import time, threading, schedule import telebot -from fortniteStatusWrapper import * -from fortniteStatusFormatter import * +from FortniteStatusWrapper import * +from FortniteStatusFormatter import * +from FortniteClient import * +from FortniteEvents import * from persistence import UserRepository if "TELEBOT_BOT_TOKEN" not in os.environ: @@ -13,6 +15,7 @@ if "TELEBOT_BOT_TOKEN" not in os.environ: bot = telebot.TeleBot(os.environ["TELEBOT_BOT_TOKEN"]) userRepository = UserRepository('db.sqlite') fortniteStatusWrapper = FortniteStatusWrapper() +fortniteClient = FortniteClient() @bot.message_handler(commands = ['start']) def startCommand(message): @@ -28,9 +31,20 @@ class FortniteStatusObserver(Observer): parse_mode='MarkdownV2' ) +class FortnitePresenceObserver(PresenceObserver): + def update(self, display_name: str, playing: bool) -> None: + for user in userRepository.getAllUsers(): + bot.send_message( + user[0], + 'Fortnite observer {}, playing {}'.format(display_name, playing), + parse_mode='MarkdownV2' + ) + if __name__ == '__main__': fortniteStatusWrapper.attach(FortniteStatusObserver()) + fortniteClient.attach(FortnitePresenceObserver()) main_thread = threading.Thread(target=bot.infinity_polling, name='bot_infinity_polling', daemon=True) main_thread.start() - main_thread.join() \ No newline at end of file + #main_thread.join() + fortniteClient.run() \ No newline at end of file -- cgit v1.2.3