wnox 0.1.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.
- wnox/__init__.py +145 -0
- wnox-0.1.0.dist-info/LICENSE.txt +0 -0
- wnox-0.1.0.dist-info/METADATA +27 -0
- wnox-0.1.0.dist-info/RECORD +6 -0
- wnox-0.1.0.dist-info/WHEEL +5 -0
- wnox-0.1.0.dist-info/top_level.txt +1 -0
wnox/__init__.py
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#%%
|
2
|
+
import asyncio
|
3
|
+
import nest_asyncio
|
4
|
+
import logging
|
5
|
+
from slixmpp import ClientXMPP
|
6
|
+
import ssl
|
7
|
+
import json
|
8
|
+
from bson import ObjectId # If you need ObjectId validation
|
9
|
+
import requests as r
|
10
|
+
|
11
|
+
nest_asyncio.apply()
|
12
|
+
# logging.basicConfig(level=logging.DEBUG)
|
13
|
+
class EventEmitter:
|
14
|
+
def __init__(self):
|
15
|
+
self._events = {}
|
16
|
+
|
17
|
+
def on(self, event_name, callback):
|
18
|
+
if event_name not in self._events:
|
19
|
+
self._events[event_name] = []
|
20
|
+
self._events[event_name].append(callback)
|
21
|
+
|
22
|
+
async def emit(self, event_name, *args, **kwargs):
|
23
|
+
if event_name in self._events:
|
24
|
+
# Collect the results of all event handlers
|
25
|
+
results = []
|
26
|
+
for callback in self._events[event_name]:
|
27
|
+
result = callback(*args, **kwargs)
|
28
|
+
if asyncio.iscoroutinefunction(callback): # If callback is async
|
29
|
+
results.append(await result)
|
30
|
+
else: # If callback is sync
|
31
|
+
results.append(result)
|
32
|
+
return results[0]
|
33
|
+
|
34
|
+
class __WebSocketXMPP(ClientXMPP, EventEmitter):
|
35
|
+
def __init__(self, jid, password):
|
36
|
+
|
37
|
+
|
38
|
+
ClientXMPP.__init__(self, jid, password)
|
39
|
+
EventEmitter.__init__(self)
|
40
|
+
print(f"connecting to {jid} - {password}")
|
41
|
+
# self.jid = jid
|
42
|
+
self.password = password
|
43
|
+
# self.callback = callback # Store the callback function
|
44
|
+
self.add_event_handler("session_start", self.start)
|
45
|
+
self.add_event_handler("failed_auth", self.on_failed_auth)
|
46
|
+
self.add_event_handler("disconnected", self.on_disconnect)
|
47
|
+
self.add_event_handler("message", self.on_message) # Add event handler for incoming messages
|
48
|
+
|
49
|
+
async def start(self, event):
|
50
|
+
"""Handle session start."""
|
51
|
+
print("[bridge] connected.")
|
52
|
+
self.send_presence()
|
53
|
+
await self.get_roster()
|
54
|
+
|
55
|
+
# Send a test message
|
56
|
+
self.send_message(mto="ethan@qepal.com", mbody="Hello via WebSocket!")
|
57
|
+
print("[📩] Message sent to ethan@qepal.com")
|
58
|
+
|
59
|
+
def on_failed_auth(self, event):
|
60
|
+
"""Handle authentication failure."""
|
61
|
+
# print("[❌] Authentication failed. Check username/password.")
|
62
|
+
|
63
|
+
def on_disconnect(self, event):
|
64
|
+
"""Handle disconnection and attempt reconnection."""
|
65
|
+
# print("[❌] Disconnected from server. Attempting to reconnect...")
|
66
|
+
asyncio.create_task(self.reconnect())
|
67
|
+
|
68
|
+
async def reconnect(self):
|
69
|
+
await asyncio.sleep(5) # Wait before reconnecting
|
70
|
+
print("[🔄] Reconnecting...")
|
71
|
+
self.connect(address=("direct.qepal.com", 5222), disable_starttls=False, force_starttls=True)
|
72
|
+
self.process(forever=False)
|
73
|
+
|
74
|
+
async def on_message(self, stanza):
|
75
|
+
"""Handle incoming messages."""
|
76
|
+
if stanza.tag == "{jabber:client}message":
|
77
|
+
body = str(stanza['body'])
|
78
|
+
from_jid = str(stanza['from'])
|
79
|
+
itsme = from_jid and f"{self.boundjid.bare.split('@')[0]}-{self.boundjid.bare.split('@')[1]}" in from_jid
|
80
|
+
itsbro = not itsme and f"{self.boundjid.bare.split('@')[0]}-" in from_jid
|
81
|
+
delayed = "urn:xmpp:delay" in str(stanza)
|
82
|
+
|
83
|
+
if body and not delayed:
|
84
|
+
user_uid = from_jid.split('@')[0]
|
85
|
+
is_app = False
|
86
|
+
if len(user_uid) != 24 or not ObjectId.is_valid(user_uid):
|
87
|
+
user_uid = user_uid.split("-")[-1]
|
88
|
+
is_app = True
|
89
|
+
|
90
|
+
if body.startswith("{"):
|
91
|
+
try:
|
92
|
+
json_data = json.loads(body)
|
93
|
+
if "api" in json_data:
|
94
|
+
data = {key: val for key, val in json_data.items() if key != "api"}
|
95
|
+
data = {key: val for key, val in data.items() if key != "mid"}
|
96
|
+
|
97
|
+
if True or len(user_uid) == 24 and ObjectId.is_valid(user_uid):
|
98
|
+
result = await self.emit(json_data["api"], data)
|
99
|
+
if result == None:
|
100
|
+
result = {}
|
101
|
+
self.send_message(
|
102
|
+
mto=from_jid,
|
103
|
+
mbody=json.dumps({**result, "mid": json_data.get("mid")})
|
104
|
+
)
|
105
|
+
else:
|
106
|
+
await self.emit("message", {"from": from_jid, "body": body, "itsme": itsme, "itsbro": itsbro, "is_app":is_app})
|
107
|
+
except json.JSONDecodeError:
|
108
|
+
pass
|
109
|
+
else:
|
110
|
+
await self.emit("message", {"from": from_jid, "body": body, "itsme": itsme, "itsbro": itsbro, "is_app":is_app})
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
class App:
|
115
|
+
def __init__(self, *, app:str, resource:str, securekey:str, image:str, public:bool=False):
|
116
|
+
self.app = app
|
117
|
+
self.resource = resource
|
118
|
+
self.securekey = securekey
|
119
|
+
self.image = image
|
120
|
+
self.public = public
|
121
|
+
|
122
|
+
json = r.post("https://qepal.com/api/bridge/worker/init", json={
|
123
|
+
"app":app, "resource":resource, "securekey":securekey, "image":image, "public":public}).json()
|
124
|
+
|
125
|
+
self.uid = json["uid"]
|
126
|
+
self.myjid = self.app + "-" + str(self.uid) + "@qepal.com/" + self.resource
|
127
|
+
self.password = json["password"]
|
128
|
+
self.xmpp = __WebSocketXMPP(self.myjid, self.password)
|
129
|
+
|
130
|
+
def on(self, api:str, cb:callable):
|
131
|
+
self.xmpp.on(api, cb)
|
132
|
+
|
133
|
+
|
134
|
+
async def loop(self):
|
135
|
+
# print("[🔄] Initializing connection...")
|
136
|
+
ssl_ctx = ssl.create_default_context()
|
137
|
+
ssl_ctx.check_hostname = False # Disable hostname verification
|
138
|
+
ssl_ctx.verify_mode = ssl.CERT_NONE # Ignore SSL certificate validation
|
139
|
+
self.xmpp.ssl_context = ssl_ctx
|
140
|
+
|
141
|
+
self.xmpp.connect(address=("direct.qepal.com", 5222), disable_starttls=False, force_starttls=True)
|
142
|
+
self.xmpp.process(forever=True) # Keep the connection alive
|
143
|
+
|
144
|
+
|
145
|
+
|
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: wnox
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A short description of your package
|
5
|
+
Home-page: https://github.com/arminkardan/pywnox
|
6
|
+
Author: Ethan (Armin) Cardan
|
7
|
+
Author-email: "Ethan (Armin) Cardan" <armin.fire@gmail.com>
|
8
|
+
Project-URL: homepage, https://github.com/arminkardan/pywnox
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Requires-Python: >=3.6
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
License-File: LICENSE.txt
|
15
|
+
Requires-Dist: asyncio
|
16
|
+
Requires-Dist: nest_asyncio
|
17
|
+
Requires-Dist: logging
|
18
|
+
Requires-Dist: slixmpp
|
19
|
+
Requires-Dist: ssl
|
20
|
+
Requires-Dist: bson
|
21
|
+
Requires-Dist: json
|
22
|
+
Requires-Dist: requests
|
23
|
+
Dynamic: author
|
24
|
+
Dynamic: home-page
|
25
|
+
Dynamic: requires-python
|
26
|
+
|
27
|
+
QE nexus client.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
wnox/__init__.py,sha256=j7NZA4FZiwhE4EAUumAR-8pWweC7Fw-Dt2QHbPCgJu4,6128
|
2
|
+
wnox-0.1.0.dist-info/LICENSE.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
wnox-0.1.0.dist-info/METADATA,sha256=xCsnfct854waMtW1yW5vfrWwWcBQzEChG7QC5vFv8mg,812
|
4
|
+
wnox-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
wnox-0.1.0.dist-info/top_level.txt,sha256=Xm9SC1bx_o6zjvo2FI-3QiZX2PZ_0UBQkGlvfYsnkwc,5
|
6
|
+
wnox-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
wnox
|