httpstate 0.0.1__tar.gz
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.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
venv
|
httpstate-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../LICENSE
|
httpstate-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: httpstate
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: HTTP State, httpstate.com
|
|
5
|
+
Author-email: "Alex Morales, HTTP State" <alex@httpstate.com>
|
|
6
|
+
License-Expression: AGPL-3.0
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: httpstate
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Requires-Dist: websockets>=16.0
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
httpstate.com
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpstate.com
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "hatchling.build"
|
|
3
|
+
requires = ["hatchling >= 1.26"]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
authors = [{ name="Alex Morales, HTTP State", email="alex@httpstate.com" }]
|
|
7
|
+
classifiers = [
|
|
8
|
+
"Operating System :: OS Independent",
|
|
9
|
+
"Programming Language :: Python :: 3"
|
|
10
|
+
]
|
|
11
|
+
dependencies = ["websockets>=16.0"]
|
|
12
|
+
description = "HTTP State, httpstate.com"
|
|
13
|
+
keywords = ["httpstate"]
|
|
14
|
+
license = "AGPL-3.0"
|
|
15
|
+
license-files = ["LICEN[CS]E*"]
|
|
16
|
+
name = "httpstate"
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
requires-python = ">=3.9"
|
|
19
|
+
version = "0.0.1"
|
|
20
|
+
|
|
21
|
+
[project.url]
|
|
22
|
+
Homepage = "https://httpstate.com"
|
|
File without changes
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# HTTP State, https://httpstate.com/
|
|
2
|
+
# Copyright (C) Alex Morales, 2026
|
|
3
|
+
|
|
4
|
+
# Unless otherwise stated in particular files or directories, this software is free software.
|
|
5
|
+
# You can redistribute it and/or modify it under the terms of the GNU Affero
|
|
6
|
+
# General Public License as published by the Free Software Foundation, either
|
|
7
|
+
# version 3 of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import threading
|
|
11
|
+
import urllib.error
|
|
12
|
+
import urllib.request
|
|
13
|
+
import websockets
|
|
14
|
+
|
|
15
|
+
from typing import Callable, Dict, List
|
|
16
|
+
|
|
17
|
+
def get(uuid:str) -> None|str:
|
|
18
|
+
try:
|
|
19
|
+
with urllib.request.urlopen(f'https://httpstate.com/{uuid}') as f:
|
|
20
|
+
if f.status == 200:
|
|
21
|
+
return f.read().decode('utf-8', 'replace')
|
|
22
|
+
|
|
23
|
+
return None
|
|
24
|
+
except urllib.error.HTTPError:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
def read(uuid:str) -> None|str:
|
|
28
|
+
return get(uuid)
|
|
29
|
+
|
|
30
|
+
def set(uuid:str, data:str) -> None|int:
|
|
31
|
+
req = urllib.request.Request(
|
|
32
|
+
f'https://httpstate.com/{uuid}',
|
|
33
|
+
data=data.encode('utf-8'),
|
|
34
|
+
headers={ 'Content-Type':'text/plain;charset=UTF-8' },
|
|
35
|
+
method='POST'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
with urllib.request.urlopen(req) as f:
|
|
40
|
+
return f.status
|
|
41
|
+
except urllib.error.HTTPError:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def write(uuid:str, data:str) -> None|int:
|
|
45
|
+
return set(uuid, data)
|
|
46
|
+
|
|
47
|
+
# HTTP State
|
|
48
|
+
class HttpState:
|
|
49
|
+
def __init__(self, uuid:str):
|
|
50
|
+
self.data:None|str = None
|
|
51
|
+
self.et:Dict[str, List[Callable[[None|str], None]]] = {}
|
|
52
|
+
self.uuid:str = uuid
|
|
53
|
+
self.ws:None|websockets.WebSocketClientProtocol = None
|
|
54
|
+
|
|
55
|
+
threading.Thread(
|
|
56
|
+
daemon=True,
|
|
57
|
+
target=lambda : asyncio.run(self._ws())
|
|
58
|
+
).start()
|
|
59
|
+
|
|
60
|
+
async def _ws(self):
|
|
61
|
+
self.ws = await websockets.connect(f"wss://httpstate.com/{self.uuid}")
|
|
62
|
+
|
|
63
|
+
await self.ws.send(f'{{"open":"{self.uuid}"}}')
|
|
64
|
+
|
|
65
|
+
async for data in self.ws:
|
|
66
|
+
data = data if isinstance(data, str) else data.decode()
|
|
67
|
+
|
|
68
|
+
if(
|
|
69
|
+
data
|
|
70
|
+
and len(data) > 32
|
|
71
|
+
and data[:32] == self.uuid
|
|
72
|
+
and data[45] == '1'
|
|
73
|
+
):
|
|
74
|
+
self.emit('change', data[46:])
|
|
75
|
+
|
|
76
|
+
def emit(self, type:str, data:None|str) -> None:
|
|
77
|
+
for callback in self.et.get(type, []):
|
|
78
|
+
callback(data)
|
|
79
|
+
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
def get(self) -> None|str:
|
|
83
|
+
return get(self.uuid)
|
|
84
|
+
|
|
85
|
+
def off(self, type:str, callback:Callable[[None|str], None]):
|
|
86
|
+
if type in self.et:
|
|
87
|
+
try:
|
|
88
|
+
self.et[type].remove(callback)
|
|
89
|
+
except ValueError:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
if not self.et[type]:
|
|
93
|
+
del self.et[type]
|
|
94
|
+
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
def on(self, type:str, callback:Callable[[None|str], None]) -> None:
|
|
98
|
+
if type not in self.et:
|
|
99
|
+
self.et[type] = []
|
|
100
|
+
|
|
101
|
+
self.et[type].append(callback)
|
|
102
|
+
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
def read(self) -> None|str:
|
|
106
|
+
return read(self.uuid)
|
|
107
|
+
|
|
108
|
+
def set(self, data:str) -> None|int:
|
|
109
|
+
return set(self.uuid, data)
|
|
110
|
+
|
|
111
|
+
def write(self, data:str) -> None|int:
|
|
112
|
+
return write(self.uuid, data)
|