wnox 0.2.0__tar.gz → 0.4.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {wnox-0.2.0 → wnox-0.4.0}/PKG-INFO +1 -7
- {wnox-0.2.0 → wnox-0.4.0}/pyproject.toml +2 -2
- {wnox-0.2.0 → wnox-0.4.0}/setup.py +1 -1
- {wnox-0.2.0 → wnox-0.4.0}/wnox/__init__.py +93 -31
- {wnox-0.2.0 → wnox-0.4.0}/wnox.egg-info/PKG-INFO +1 -7
- wnox-0.4.0/wnox.egg-info/requires.txt +2 -0
- wnox-0.2.0/wnox.egg-info/requires.txt +0 -8
- {wnox-0.2.0 → wnox-0.4.0}/LICENSE.txt +0 -0
- {wnox-0.2.0 → wnox-0.4.0}/README.md +0 -0
- {wnox-0.2.0 → wnox-0.4.0}/setup.cfg +0 -0
- {wnox-0.2.0 → wnox-0.4.0}/wnox.egg-info/SOURCES.txt +0 -0
- {wnox-0.2.0 → wnox-0.4.0}/wnox.egg-info/dependency_links.txt +0 -0
- {wnox-0.2.0 → wnox-0.4.0}/wnox.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: wnox
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A short description of your package
|
5
5
|
Home-page: https://github.com/arminkardan/pywnox
|
6
6
|
Author: Ethan (Armin) Cardan
|
@@ -12,14 +12,8 @@ Classifier: Operating System :: OS Independent
|
|
12
12
|
Requires-Python: >=3.12
|
13
13
|
Description-Content-Type: text/markdown
|
14
14
|
License-File: LICENSE.txt
|
15
|
-
Requires-Dist: asyncio
|
16
|
-
Requires-Dist: nest_asyncio
|
17
|
-
Requires-Dist: logging
|
18
15
|
Requires-Dist: slixmpp
|
19
|
-
Requires-Dist: ssl
|
20
16
|
Requires-Dist: bson
|
21
|
-
Requires-Dist: json
|
22
|
-
Requires-Dist: requests
|
23
17
|
Dynamic: author
|
24
18
|
Dynamic: home-page
|
25
19
|
Dynamic: requires-python
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "wnox"
|
7
|
-
version = "0.
|
7
|
+
version = "0.4.0"
|
8
8
|
authors = [{ name = "Ethan (Armin) Cardan", email = "armin.fire@gmail.com" }]
|
9
9
|
description = "A short description of your package"
|
10
10
|
readme = "README.md"
|
11
11
|
license = { file = "LICENSE" }
|
12
|
-
dependencies = ["
|
12
|
+
dependencies = ["slixmpp","bson"]
|
13
13
|
requires-python = ">=3.12"
|
14
14
|
classifiers = [
|
15
15
|
"Programming Language :: Python :: 3",
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: wnox
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A short description of your package
|
5
5
|
Home-page: https://github.com/arminkardan/pywnox
|
6
6
|
Author: Ethan (Armin) Cardan
|
@@ -12,14 +12,8 @@ Classifier: Operating System :: OS Independent
|
|
12
12
|
Requires-Python: >=3.12
|
13
13
|
Description-Content-Type: text/markdown
|
14
14
|
License-File: LICENSE.txt
|
15
|
-
Requires-Dist: asyncio
|
16
|
-
Requires-Dist: nest_asyncio
|
17
|
-
Requires-Dist: logging
|
18
15
|
Requires-Dist: slixmpp
|
19
|
-
Requires-Dist: ssl
|
20
16
|
Requires-Dist: bson
|
21
|
-
Requires-Dist: json
|
22
|
-
Requires-Dist: requests
|
23
17
|
Dynamic: author
|
24
18
|
Dynamic: home-page
|
25
19
|
Dynamic: requires-python
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|