wnox 0.3.0__py3-none-any.whl → 0.5.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 +93 -31
- {wnox-0.3.0.dist-info → wnox-0.5.0.dist-info}/METADATA +1 -1
- wnox-0.5.0.dist-info/RECORD +6 -0
- wnox-0.3.0.dist-info/RECORD +0 -6
- {wnox-0.3.0.dist-info → wnox-0.5.0.dist-info}/LICENSE.txt +0 -0
- {wnox-0.3.0.dist-info → wnox-0.5.0.dist-info}/WHEEL +0 -0
- {wnox-0.3.0.dist-info → wnox-0.5.0.dist-info}/top_level.txt +0 -0
wnox/__init__.py
CHANGED
@@ -5,11 +5,22 @@ import logging
|
|
5
5
|
from slixmpp import ClientXMPP
|
6
6
|
import ssl
|
7
7
|
import json
|
8
|
-
from bson import ObjectId
|
8
|
+
from bson import ObjectId
|
9
9
|
import requests as r
|
10
10
|
|
11
|
+
import random
|
12
|
+
import string
|
13
|
+
|
11
14
|
nest_asyncio.apply()
|
15
|
+
eventdatax = {}
|
16
|
+
eventsx = {}
|
12
17
|
# logging.basicConfig(level=logging.DEBUG)
|
18
|
+
|
19
|
+
def serial_generator(length: int) -> str:
|
20
|
+
chars = string.digits + string.ascii_uppercase + string.ascii_lowercase
|
21
|
+
random_string = ''.join(random.choice(chars) for _ in range(length))
|
22
|
+
return random_string
|
23
|
+
|
13
24
|
class EventEmitter:
|
14
25
|
def __init__(self):
|
15
26
|
self._events = {}
|
@@ -31,43 +42,41 @@ class EventEmitter:
|
|
31
42
|
results.append(result)
|
32
43
|
return results[0]
|
33
44
|
|
34
|
-
class
|
35
|
-
|
45
|
+
class WSX(ClientXMPP, EventEmitter):
|
46
|
+
connected = False
|
47
|
+
def __init__(self, jid, password, app:str, uid:str, resource:str):
|
36
48
|
|
37
|
-
|
38
49
|
ClientXMPP.__init__(self, jid, password)
|
39
50
|
EventEmitter.__init__(self)
|
40
|
-
|
41
|
-
|
51
|
+
self.app = app
|
52
|
+
self.uid = uid
|
53
|
+
self._resource = resource
|
42
54
|
self.password = password
|
43
|
-
# self.callback = callback # Store the callback function
|
44
55
|
self.add_event_handler("session_start", self.start)
|
45
56
|
self.add_event_handler("failed_auth", self.on_failed_auth)
|
46
57
|
self.add_event_handler("disconnected", self.on_disconnect)
|
47
|
-
self.add_event_handler("message", self.on_message)
|
58
|
+
self.add_event_handler("message", self.on_message)
|
48
59
|
|
49
60
|
async def start(self, event):
|
50
61
|
"""Handle session start."""
|
51
62
|
print("[bridge] connected.")
|
52
|
-
self.send_presence()
|
63
|
+
self.send_presence(ptype="presence")
|
53
64
|
await self.get_roster()
|
65
|
+
await self.emit("connect",{})
|
66
|
+
self.connected = True
|
54
67
|
|
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
68
|
|
59
69
|
def on_failed_auth(self, event):
|
60
70
|
"""Handle authentication failure."""
|
61
|
-
# print("[❌] Authentication failed. Check username/password.")
|
62
71
|
|
63
|
-
def on_disconnect(self, event):
|
72
|
+
async def on_disconnect(self, event):
|
64
73
|
"""Handle disconnection and attempt reconnection."""
|
65
|
-
|
74
|
+
await self.emit("disconnect",{})
|
75
|
+
self.connected = False
|
66
76
|
asyncio.create_task(self.reconnect())
|
67
77
|
|
68
78
|
async def reconnect(self):
|
69
|
-
await asyncio.sleep(5)
|
70
|
-
print("[🔄] Reconnecting...")
|
79
|
+
await asyncio.sleep(5)
|
71
80
|
self.connect(address=("direct.qepal.com", 5222), disable_starttls=False, force_starttls=True)
|
72
81
|
self.process(forever=False)
|
73
82
|
|
@@ -78,14 +87,15 @@ class __WebSocketXMPP(ClientXMPP, EventEmitter):
|
|
78
87
|
from_jid = str(stanza['from'])
|
79
88
|
itsme = from_jid and f"{self.boundjid.bare.split('@')[0]}-{self.boundjid.bare.split('@')[1]}" in from_jid
|
80
89
|
itsbro = not itsme and f"{self.boundjid.bare.split('@')[0]}-" in from_jid
|
90
|
+
|
91
|
+
if "conference.qepal.com" in from_jid:
|
92
|
+
itsme = f"{self.app}-{self.uid}-{self._resource}" in from_jid
|
93
|
+
itsbro = not itsme and f"{self.app}-{self.uid}-" in from_jid
|
94
|
+
|
81
95
|
delayed = "urn:xmpp:delay" in str(stanza)
|
82
96
|
|
83
97
|
if body and not delayed:
|
84
98
|
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
99
|
|
90
100
|
if body.startswith("{"):
|
91
101
|
try:
|
@@ -103,43 +113,95 @@ class __WebSocketXMPP(ClientXMPP, EventEmitter):
|
|
103
113
|
mbody=json.dumps({**result, "mid": json_data.get("mid")})
|
104
114
|
)
|
105
115
|
else:
|
106
|
-
|
116
|
+
if "mid" in json_data:
|
117
|
+
data = {key: val for key, val in json_data.items() if key != "mid"}
|
118
|
+
eventdatax[json_data.get("mid")] = data
|
119
|
+
if json_data.get("mid") in eventsx:
|
120
|
+
eventsx.get(json_data.get("mid")).set()
|
121
|
+
else:
|
122
|
+
await self.emit("message", {"from": from_jid, "body": body, "itsme": itsme, "itsbro": itsbro})
|
123
|
+
|
107
124
|
except json.JSONDecodeError:
|
108
125
|
pass
|
109
126
|
else:
|
110
|
-
await self.emit("message", {"from": from_jid, "body": body, "itsme": itsme, "itsbro": itsbro
|
127
|
+
await self.emit("message", {"from": from_jid, "body": body, "itsme": itsme, "itsbro": itsbro})
|
111
128
|
|
112
129
|
|
113
130
|
|
114
131
|
class App:
|
132
|
+
|
115
133
|
def __init__(self, *, app:str, resource:str, securekey:str, image:str, public:bool=False):
|
116
134
|
self.app = app
|
135
|
+
self.channels = set()
|
117
136
|
self.resource = resource
|
118
137
|
self.securekey = securekey
|
119
138
|
self.image = image
|
120
139
|
self.public = public
|
121
140
|
|
122
|
-
|
141
|
+
_json = r.post("https://qepal.com/api/bridge/worker/init", json={
|
123
142
|
"app":app, "resource":resource, "securekey":securekey, "image":image, "public":public}).json()
|
124
143
|
|
125
|
-
self.uid =
|
144
|
+
self.uid = _json["uid"]
|
126
145
|
self.myjid = self.app + "-" + str(self.uid) + "@qepal.com/" + self.resource
|
127
|
-
self.password =
|
128
|
-
self.xmpp =
|
146
|
+
self.password = _json["password"]
|
147
|
+
self.xmpp = WSX(self.myjid, self.password, self.app, self.uid, self.resource)
|
129
148
|
|
130
149
|
def on(self, api:str, cb:callable):
|
131
150
|
self.xmpp.on(api, cb)
|
132
151
|
|
152
|
+
def sendtojid(self, jid:str, body:str):
|
153
|
+
self.xmpp.send_message(mto=jid, mbody=body)
|
154
|
+
|
155
|
+
def connected(self):
|
156
|
+
return self.xmpp.connected
|
157
|
+
|
158
|
+
async def api(self, app:str, cmd:str, body:dict, jid:str = None, prioritize_public:bool = False):
|
159
|
+
if jid == None:
|
160
|
+
res:dict = r.post("https://qepal.com/api/bridge/worker/findfreeresource", json={ "app":app, "securekey": self.securekey }).json()
|
161
|
+
jids = list(res.get("jids",[]))
|
162
|
+
if len(jids) > 0:
|
163
|
+
if prioritize_public:
|
164
|
+
jid = jids[-1]
|
165
|
+
else:
|
166
|
+
jid = jids[0]
|
167
|
+
if jid == None:
|
168
|
+
print("unable to send api (-1)")
|
169
|
+
return
|
170
|
+
|
171
|
+
mid = serial_generator(10)
|
172
|
+
msg = {"mid":mid, "api":cmd, **body }
|
173
|
+
eventsx[mid] = asyncio.Event()
|
174
|
+
self.sendtojid(jid, json.dumps(msg))
|
175
|
+
await eventsx[mid].wait()
|
176
|
+
data = eventdatax.get(mid)
|
177
|
+
return data
|
178
|
+
|
179
|
+
|
180
|
+
|
181
|
+
def subscribe(self, channelname:str):
|
182
|
+
self.xmpp.send_presence(pto=channelname+ "@conference.qepal.com/"+ self.app + "-" + self.uid + "-" + self.resource , ptype="presence")
|
183
|
+
self.xmpp.get_roster()
|
184
|
+
self.channels.add(channelname)
|
185
|
+
|
186
|
+
def unsubscribe(self, channelname:str):
|
187
|
+
self.xmpp.send_presence(pto=channelname+ "@conference.qepal.com" , ptype="unavailable")
|
188
|
+
self.xmpp.get_roster()
|
189
|
+
self.channels.remove(channelname)
|
190
|
+
|
191
|
+
def sendtochannel(self, channelname:str, body:str):
|
192
|
+
if channelname not in self.channels:
|
193
|
+
self.subscribe(channelname)
|
194
|
+
self.xmpp.send_message(mto=f"{channelname}@conference.qepal.com", mbody=body, mtype='groupchat')
|
133
195
|
|
134
196
|
async def loop(self):
|
135
|
-
|
197
|
+
|
136
198
|
ssl_ctx = ssl.create_default_context()
|
137
|
-
ssl_ctx.check_hostname = False
|
138
|
-
ssl_ctx.verify_mode = ssl.CERT_NONE
|
199
|
+
ssl_ctx.check_hostname = False
|
200
|
+
ssl_ctx.verify_mode = ssl.CERT_NONE
|
139
201
|
self.xmpp.ssl_context = ssl_ctx
|
140
202
|
|
141
203
|
self.xmpp.connect(address=("direct.qepal.com", 5222), disable_starttls=False, force_starttls=True)
|
142
|
-
self.xmpp.process(forever=True)
|
204
|
+
self.xmpp.process(forever=True)
|
143
205
|
|
144
206
|
|
145
207
|
|
@@ -0,0 +1,6 @@
|
|
1
|
+
wnox/__init__.py,sha256=_M92Wza8dl4QeQWdM0ckRtphgyfZs9g4DqwwvgSvOKI,8234
|
2
|
+
wnox-0.5.0.dist-info/LICENSE.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
wnox-0.5.0.dist-info/METADATA,sha256=JK1RXI-Ep2tmgbAmGiIkbCCwBMWwyjGrBZ6oXYWrfZE,670
|
4
|
+
wnox-0.5.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
wnox-0.5.0.dist-info/top_level.txt,sha256=Xm9SC1bx_o6zjvo2FI-3QiZX2PZ_0UBQkGlvfYsnkwc,5
|
6
|
+
wnox-0.5.0.dist-info/RECORD,,
|
wnox-0.3.0.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
wnox/__init__.py,sha256=j7NZA4FZiwhE4EAUumAR-8pWweC7Fw-Dt2QHbPCgJu4,6128
|
2
|
-
wnox-0.3.0.dist-info/LICENSE.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
wnox-0.3.0.dist-info/METADATA,sha256=4jzCO_HyB0cIHeSrEg4MvYuVibx0goO1S_Hgqk2yyJI,670
|
4
|
-
wnox-0.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
-
wnox-0.3.0.dist-info/top_level.txt,sha256=Xm9SC1bx_o6zjvo2FI-3QiZX2PZ_0UBQkGlvfYsnkwc,5
|
6
|
-
wnox-0.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|