albibong 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. albibong/__init__.py +82 -0
  2. albibong/classes/__init__.py +0 -0
  3. albibong/classes/character.py +83 -0
  4. albibong/classes/coords.py +9 -0
  5. albibong/classes/dungeon.py +59 -0
  6. albibong/classes/item.py +41 -0
  7. albibong/classes/location.py +48 -0
  8. albibong/classes/logger.py +122 -0
  9. albibong/classes/packet_handler.py +61 -0
  10. albibong/classes/world_data.py +327 -0
  11. albibong/gui_dist/No Equipment.png +0 -0
  12. albibong/gui_dist/assets/index-BFSx0nua.css +1 -0
  13. albibong/gui_dist/assets/index-DAndaN_4.js +161 -0
  14. albibong/gui_dist/fame.png +0 -0
  15. albibong/gui_dist/index.html +14 -0
  16. albibong/gui_dist/re_spec.png +0 -0
  17. albibong/gui_dist/silver.png +0 -0
  18. albibong/gui_dist/vite.svg +1 -0
  19. albibong/photon_packet_parser/__init__.py +4 -0
  20. albibong/photon_packet_parser/command_type.py +7 -0
  21. albibong/photon_packet_parser/crc_calculator.py +16 -0
  22. albibong/photon_packet_parser/event_data.py +4 -0
  23. albibong/photon_packet_parser/message_type.py +6 -0
  24. albibong/photon_packet_parser/number_serializer.py +22 -0
  25. albibong/photon_packet_parser/operation_request.py +4 -0
  26. albibong/photon_packet_parser/operation_response.py +6 -0
  27. albibong/photon_packet_parser/photon_packet_parser.py +168 -0
  28. albibong/photon_packet_parser/protocol16_deserializer.py +302 -0
  29. albibong/photon_packet_parser/protocol16_type.py +23 -0
  30. albibong/photon_packet_parser/segmented_packet.py +5 -0
  31. albibong/resources/EventCode.py +579 -0
  32. albibong/resources/OperationCode.py +499 -0
  33. albibong/resources/event_code.json +577 -0
  34. albibong/resources/items.json +54035 -0
  35. albibong/resources/maps.json +3565 -0
  36. albibong/resources/operation_code.json +497 -0
  37. albibong/threads/__init__.py +0 -0
  38. albibong/threads/http_server.py +60 -0
  39. albibong/threads/packet_handler_thread.py +32 -0
  40. albibong/threads/sniffer_thread.py +27 -0
  41. albibong/threads/websocket_server.py +168 -0
  42. albibong-1.0.0.dist-info/METADATA +15 -0
  43. albibong-1.0.0.dist-info/RECORD +46 -0
  44. albibong-1.0.0.dist-info/WHEEL +4 -0
  45. albibong-1.0.0.dist-info/entry_points.txt +2 -0
  46. albibong-1.0.0.dist-info/licenses/LICENSE +19 -0
albibong/__init__.py ADDED
@@ -0,0 +1,82 @@
1
+ import queue
2
+ import sys
3
+ from time import sleep
4
+
5
+ import webview
6
+ from scapy.all import rdpcap
7
+
8
+ from albibong.classes.logger import Logger
9
+ from albibong.classes.packet_handler import PacketHandler
10
+ from albibong.threads.http_server import HttpServerThread
11
+ from albibong.threads.packet_handler_thread import PacketHandlerThread
12
+ from albibong.threads.sniffer_thread import SnifferThread
13
+ from albibong.threads.websocket_server import get_ws_server
14
+
15
+ logger = Logger(__name__, stdout=True, log_to_file=False)
16
+
17
+
18
+ def read_pcap(path):
19
+ packet_handler = PacketHandler()
20
+ scapy_cap = rdpcap(path)
21
+ for packet in scapy_cap:
22
+ packet_handler.handle(packet)
23
+
24
+
25
+ def sniff():
26
+ _sentinel = object()
27
+ packet_queue = queue.Queue()
28
+
29
+ p = SnifferThread(name="sniffer", out_queue=packet_queue, sentinel=_sentinel)
30
+
31
+ c = PacketHandlerThread(
32
+ name="packet_handler",
33
+ in_queue=packet_queue,
34
+ sentinel=_sentinel,
35
+ )
36
+
37
+ p.start()
38
+ c.start()
39
+
40
+ ws_server = get_ws_server()
41
+ ws_server.start()
42
+
43
+ http_server = HttpServerThread(name="http_server")
44
+ http_server.start()
45
+
46
+ # sleep(0.25)
47
+ # with open("../gui/dist/index.html", "r") as html:
48
+ window = webview.create_window(
49
+ "Albibong",
50
+ # "../../gui/dist/index.html",
51
+ url="http://localhost:8000/",
52
+ width=1280,
53
+ height=720,
54
+ zoomable=True,
55
+ )
56
+
57
+ def on_closing():
58
+ c.stop()
59
+ p.stop()
60
+ ws_server.stop()
61
+ http_server.stop()
62
+
63
+ window.events.closing += on_closing
64
+
65
+ webview.start()
66
+
67
+
68
+ def main():
69
+ if len(sys.argv) > 1:
70
+ ws_server = get_ws_server()
71
+ ws_server.start()
72
+
73
+ http_server = HttpServerThread(name="http_server")
74
+ http_server.start()
75
+
76
+ read_pcap(sys.argv[1])
77
+ else:
78
+ sniff()
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
File without changes
@@ -0,0 +1,83 @@
1
+ import json
2
+
3
+ from albibong.classes.coords import Coords
4
+ from albibong.classes.item import Item
5
+ from albibong.threads.websocket_server import send_event
6
+
7
+ DIVISOR = 10000
8
+
9
+
10
+ class Character:
11
+
12
+ def __init__(
13
+ self,
14
+ id: int,
15
+ username: str,
16
+ guild: str,
17
+ alliance: str,
18
+ coords: Coords,
19
+ equipment: list[Item] = [Item.get_item_from_code("0")] * 10,
20
+ ):
21
+ self.id = id
22
+ self.username = username
23
+ self.guild = guild
24
+ self.alliance = alliance
25
+ self.coords = coords
26
+ self.fame_gained: int = 0
27
+ self.re_spec_gained: int = 0
28
+ self.silver_gained: int = 0
29
+ self.damage_dealt: int = 0
30
+ self.healing_dealt: int = 0
31
+ self.loot: list[str] = []
32
+ self.equipment = equipment
33
+
34
+ def handle_health_update(self, parameters):
35
+ if 2 in parameters:
36
+ if parameters[2] < 0:
37
+ if parameters[0] != self.id:
38
+ self.damage_dealt += abs(parameters[2])
39
+ else:
40
+ self.healing_dealt += abs(parameters[2])
41
+
42
+ def update_coords(self, parameters):
43
+ if 3 in parameters:
44
+ self.coords = Coords(parameters[3][0], parameters[3][1])
45
+
46
+ def update_fame(self, parameters):
47
+ fame = parameters[2] / DIVISOR
48
+ self.fame_gained += fame
49
+ event = {
50
+ "type": "update_fame",
51
+ "payload": {"username": self.username, "fame_gained": fame},
52
+ }
53
+ send_event(event)
54
+
55
+ def update_re_spec(self, parameters):
56
+ re_spec = parameters[2] / DIVISOR
57
+ self.re_spec_gained += re_spec
58
+ event = {
59
+ "type": "update_re_spec",
60
+ "payload": {"username": self.username, "re_spec_gained": re_spec},
61
+ }
62
+ send_event(event)
63
+
64
+ def update_loot(self, parameters):
65
+ if 3 in parameters and parameters[3] == True:
66
+ if 5 in parameters:
67
+ silver = parameters[5] / DIVISOR
68
+ self.silver_gained += silver
69
+ event = {
70
+ "type": "update_silver",
71
+ "payload": {"username": self.username, "silver_gained": silver},
72
+ }
73
+ send_event(event)
74
+ else:
75
+ self.loot.append(parameters[4])
76
+
77
+ def update_equipment(self, equipments):
78
+ new_eq = []
79
+ if equipments != []:
80
+ for eq in equipments:
81
+ obj = Item.get_item_from_code(str(eq))
82
+ new_eq.append(obj)
83
+ self.equipment = new_eq
@@ -0,0 +1,9 @@
1
+ class Coords:
2
+
3
+ def __init__(self, x, y):
4
+ self.x: float = x
5
+ self.y: float = y
6
+
7
+ @staticmethod
8
+ def serialize(coords):
9
+ return {"x": coords.x, "y": coords.y}
@@ -0,0 +1,59 @@
1
+ from datetime import datetime
2
+ import uuid
3
+
4
+ DIVISOR = 10000
5
+
6
+
7
+ class Dungeon:
8
+
9
+ def __init__(self, type, name) -> None:
10
+ self.id = uuid.uuid4()
11
+ self.type = type
12
+ self.name = name
13
+ self.tier = 0
14
+ self.fame_gained = 0
15
+ self.silver_gained = 0
16
+ self.re_spec_gained = 0
17
+ self.loot = []
18
+ self.start_time = datetime.now()
19
+ self.time_elapsed = 0
20
+
21
+ @staticmethod
22
+ def serialize(dungeon):
23
+ return {
24
+ "id": str(dungeon.id),
25
+ "type": dungeon.type,
26
+ "name": dungeon.name,
27
+ "tier": dungeon.tier,
28
+ "fame": dungeon.fame_gained,
29
+ "silver": dungeon.silver_gained,
30
+ "re_spec": dungeon.re_spec_gained,
31
+ "date_time": dungeon.start_time.strftime("%a %d %b %Y, %I:%M%p"),
32
+ "time_elapsed": str(dungeon.time_elapsed).split(".")[0],
33
+ }
34
+
35
+ @staticmethod
36
+ def serialize_many(dungeons):
37
+ serialized = []
38
+ for key, value in dungeons.items():
39
+ serialized.append(Dungeon.serialize(value))
40
+ return serialized
41
+
42
+ def get_elapsed_time(self):
43
+ end_time = datetime.now()
44
+ self.time_elapsed = end_time - self.start_time
45
+
46
+ def update_fame(self, parameters):
47
+ fame = parameters[2] / DIVISOR
48
+ self.fame_gained += fame
49
+
50
+ def update_re_spec(self, parameters):
51
+ re_spec = parameters[2] / DIVISOR
52
+ self.re_spec_gained += re_spec
53
+
54
+ def update_loot(self, parameters):
55
+ if 3 in parameters and parameters[3] == True:
56
+ silver = parameters[5] / DIVISOR
57
+ self.silver_gained += silver
58
+ else:
59
+ self.loot.append(parameters[4])
@@ -0,0 +1,41 @@
1
+ import json
2
+ import os
3
+
4
+ itemsJsonPath = os.path.join(os.path.dirname(__file__), "../resources/items.json")
5
+ with open(itemsJsonPath) as json_file:
6
+ item_data = json.load(json_file)
7
+
8
+
9
+ class Item:
10
+
11
+ def __init__(self, id: str, name: str, unique_name: str):
12
+ self.id = id
13
+ self.name = name
14
+ self.unique_name = unique_name
15
+ self.image = (
16
+ "https://render.albiononline.com/v1/item/" + self.unique_name
17
+ if self.unique_name != "None"
18
+ else "../public/No Equipment.png"
19
+ )
20
+
21
+ @staticmethod
22
+ def serialize(item):
23
+ return {
24
+ "id": item.id,
25
+ "name": item.name,
26
+ "unique_name": item.unique_name,
27
+ "image": item.image,
28
+ }
29
+
30
+ @staticmethod
31
+ def serialize_many(items):
32
+ serialized = []
33
+ for item in items:
34
+ serialized.append(Item.serialize(item))
35
+ return serialized
36
+
37
+ @classmethod
38
+ def get_item_from_code(cls, code: str):
39
+ item = item_data[code]
40
+
41
+ return cls(id=item["id"], name=item["name"], unique_name=item["unique_name"])
@@ -0,0 +1,48 @@
1
+ import json
2
+ import os
3
+
4
+ mapsJsonPath = os.path.join(os.path.dirname(__file__), "../resources/maps.json")
5
+ with open(mapsJsonPath) as json_file:
6
+ map_data = json.load(json_file)
7
+
8
+
9
+ class Location:
10
+
11
+ def __init__(self, id: str, name: str, type: str):
12
+ self.id = id
13
+ self.name = name
14
+ self.type = type
15
+
16
+ def is_black_zone(self):
17
+ if self.type == "OPENPVP_BLACK":
18
+ return True
19
+ elif self.type == "DUNGEON_BLACK":
20
+ return True
21
+ elif self.type == "PASSAGE_BLACK":
22
+ return True
23
+ elif self.type == "TUNNEL":
24
+ return True
25
+ else:
26
+ return False
27
+
28
+ def is_red_zone(self):
29
+ if self.type == "OPENPVP_RED":
30
+ return True
31
+ elif self.type == "DUNGEON_RED":
32
+ return True
33
+ elif self.type == "PASSAGE_RED":
34
+ return True
35
+ else:
36
+ return False
37
+
38
+ def is_safe_zone(self):
39
+ if self.is_black_zone() == False and self.is_red_zone() == False:
40
+ return True
41
+ else:
42
+ return False
43
+
44
+ @classmethod
45
+ def get_location_from_code(cls, code: str):
46
+ location = map_data[code]
47
+
48
+ return cls(id=location["id"], name=location["name"], type=location["type"])
@@ -0,0 +1,122 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from datetime import datetime, timezone
5
+
6
+ from scapy.all import wrpcap
7
+
8
+ from albibong.photon_packet_parser import EventData, OperationRequest, OperationResponse
9
+
10
+
11
+ class Logger(logging.Logger):
12
+ def __init__(
13
+ self,
14
+ name,
15
+ stdout=False,
16
+ log_to_file=True,
17
+ ignore_event=False,
18
+ ignore_request=False,
19
+ ignore_response=False,
20
+ ):
21
+ super().__init__(name)
22
+ self.ignore_event = ignore_event
23
+ self.ignore_request = ignore_request
24
+ self.ignore_response = ignore_response
25
+
26
+ self.file_timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H%M%S")
27
+ log_filepath = os.path.join(
28
+ os.path.expanduser("~"), f"Albibong/log/log_{self.file_timestamp}.txt"
29
+ )
30
+ os.makedirs(os.path.dirname(log_filepath), exist_ok=True)
31
+
32
+ self.setLevel(logging.DEBUG)
33
+ log_formatter = logging.Formatter("%(message)s")
34
+
35
+ if log_to_file:
36
+ file_handler = logging.FileHandler(log_filepath)
37
+ file_handler.setFormatter(log_formatter)
38
+ file_handler.setLevel(logging.INFO)
39
+ self.addHandler(file_handler)
40
+
41
+ if stdout:
42
+ stdout_handler = logging.StreamHandler()
43
+ stdout_handler.setFormatter(log_formatter)
44
+ stdout_handler.setLevel(logging.INFO)
45
+ self.addHandler(stdout_handler)
46
+
47
+ # Load Event and Operation Code for logging
48
+ eventCodeJsonPath = os.path.join(
49
+ os.path.dirname(__file__), "../resources/event_code.json"
50
+ )
51
+ with open(eventCodeJsonPath) as json_file:
52
+ data = json.load(json_file)
53
+ self.event_code = data
54
+
55
+ operationCodeJsonPath = os.path.join(
56
+ os.path.dirname(__file__), "../resources/operation_code.json"
57
+ )
58
+ with open(operationCodeJsonPath) as json_file:
59
+ data = json.load(json_file)
60
+ self.operation_code = data
61
+
62
+ def get_timestamp(self):
63
+ return datetime.now(timezone.utc).strftime("%H:%M:%S")
64
+
65
+ def log_payload(self, payload):
66
+ if type(payload) == EventData:
67
+ self.log_event(payload)
68
+ elif type(payload) == OperationRequest:
69
+ self.log_request(payload)
70
+ elif type(payload) == OperationResponse:
71
+ self.log_response(payload)
72
+
73
+ def log_dps_meter_state(self, state: bool):
74
+ text = f"{self.get_timestamp()} DPS METER IS {'RUNNING. The damages below this point will be recorded.' if state else 'PAUSED. The damages below this point will NOT be recorded.'}"
75
+ self.info(text)
76
+ print(text)
77
+
78
+ def log_event(self, payload: EventData):
79
+ if self.ignore_event:
80
+ return
81
+
82
+ # Ignore position update, timesync, and guild updates
83
+ if payload.code == 3:
84
+ return
85
+
86
+ parameters = payload.parameters
87
+ if 252 in parameters and parameters[252] in {99, 100, 152}:
88
+ return
89
+
90
+ event_code = str(parameters[252]) if 252 in parameters else None
91
+ if event_code in self.event_code:
92
+ event_code = self.event_code[event_code]
93
+
94
+ self.info(f"{self.get_timestamp()} event {event_code}")
95
+ self.info(parameters)
96
+
97
+ def log_request(self, payload: OperationRequest):
98
+ if self.ignore_request:
99
+ return
100
+
101
+ parameters = payload.parameters
102
+ operation_code = str(parameters[253]) if 253 in parameters else None
103
+ if operation_code in self.operation_code:
104
+ operation_code = self.operation_code[operation_code]
105
+
106
+ self.info(f"{self.get_timestamp()} request {operation_code}")
107
+ self.info(parameters)
108
+
109
+ def log_response(self, payload: OperationResponse):
110
+ if self.ignore_response:
111
+ return
112
+
113
+ parameters = payload.parameters
114
+ operation_code = str(parameters[253]) if 253 in parameters else None
115
+ if operation_code in self.operation_code:
116
+ operation_code = self.operation_code[operation_code]
117
+
118
+ self.info(f"{self.get_timestamp()} response {operation_code}")
119
+ self.info(parameters)
120
+
121
+ def log_error_pcap(self, packet):
122
+ wrpcap(f"../pcap/error_{self.file_timestamp}.pcapng", packet, append=True)
@@ -0,0 +1,61 @@
1
+ import functools
2
+ import traceback
3
+
4
+ from scapy.all import UDP, Packet
5
+
6
+ from albibong.classes.logger import Logger
7
+ from albibong.classes.world_data import WorldData, get_world_data
8
+ from albibong.photon_packet_parser import (
9
+ EventData,
10
+ OperationRequest,
11
+ OperationResponse,
12
+ PhotonPacketParser,
13
+ )
14
+
15
+ logger = Logger(__name__, ignore_request=True)
16
+
17
+
18
+ def log_payload(func):
19
+ @functools.wraps(func)
20
+ def wrapper(*args, **kwargs):
21
+ try:
22
+ payload = func(*args, **kwargs)
23
+ logger.log_payload(payload)
24
+ except:
25
+ print(f"ERROR {func.__name__}: {traceback.format_exc()}")
26
+
27
+ return wrapper
28
+
29
+
30
+ class PacketHandler:
31
+ def __init__(self):
32
+ self.parser = PhotonPacketParser(
33
+ self.on_event, self.on_request, self.on_response
34
+ )
35
+ self.world_data = get_world_data()
36
+
37
+ @log_payload
38
+ def on_event(self, payload: EventData):
39
+ self.world_data.handle_event(payload.parameters)
40
+ return payload
41
+
42
+ @log_payload
43
+ def on_request(self, payload: OperationRequest):
44
+ self.world_data.handle_request(payload.parameters)
45
+ return payload
46
+
47
+ @log_payload
48
+ def on_response(self, payload: OperationResponse):
49
+ self.world_data.handle_response(payload.parameters)
50
+ return payload
51
+
52
+ def handle(self, packet: Packet):
53
+ try:
54
+ payload = packet[UDP].payload.original
55
+ self.parser.handle_payload(payload)
56
+ except Exception as e:
57
+ if type(e) == KeyboardInterrupt:
58
+ raise e
59
+
60
+ print(f"ERROR HANDLE: {traceback.format_exc()}")
61
+ logger.log_error_pcap(packet)