onyx-and-iris bu gisti düzenledi . Düzenlemeye git
1 file changed, 4 insertions, 3 deletions
sendtext.py
| @@ -164,14 +164,15 @@ class VbanSendText: | |||
| 164 | 164 | self.__dict__.update(defaultkwargs) | |
| 165 | 165 | self._request = RequestPacket(RTHeader(self.streamname, self.bps, self.channel)) | |
| 166 | 166 | self.lastsent = 0 | |
| 167 | - | self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| 168 | 167 | ||
| 169 | 168 | def __enter__(self): | |
| 170 | - | self._sock.__enter__() | |
| 169 | + | self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| 171 | 170 | return self | |
| 172 | 171 | ||
| 173 | 172 | def __exit__(self, exc_type, exc_value, traceback): | |
| 174 | - | return self._sock.__exit__(exc_type, exc_value, traceback) | |
| 173 | + | if self._sock: | |
| 174 | + | self._sock.close() | |
| 175 | + | self._sock = None | |
| 175 | 176 | ||
| 176 | 177 | @ratelimit | |
| 177 | 178 | def sendtext(self, text: str): | |
onyx-and-iris bu gisti düzenledi . Düzenlemeye git
1 file changed, 3 insertions, 2 deletions
sendtext.py
| @@ -164,13 +164,14 @@ class VbanSendText: | |||
| 164 | 164 | self.__dict__.update(defaultkwargs) | |
| 165 | 165 | self._request = RequestPacket(RTHeader(self.streamname, self.bps, self.channel)) | |
| 166 | 166 | self.lastsent = 0 | |
| 167 | + | self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| 167 | 168 | ||
| 168 | 169 | def __enter__(self): | |
| 169 | - | self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| 170 | + | self._sock.__enter__() | |
| 170 | 171 | return self | |
| 171 | 172 | ||
| 172 | 173 | def __exit__(self, exc_type, exc_value, traceback): | |
| 173 | - | self._sock.close() | |
| 174 | + | return self._sock.__exit__(exc_type, exc_value, traceback) | |
| 174 | 175 | ||
| 175 | 176 | @ratelimit | |
| 176 | 177 | def sendtext(self, text: str): | |
onyx-and-iris bu gisti düzenledi . Düzenlemeye git
1 file changed, 58 insertions, 34 deletions
sendtext.py
| @@ -22,6 +22,30 @@ | |||
| 22 | 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 23 | 23 | # SOFTWARE. | |
| 24 | 24 | ||
| 25 | + | """ | |
| 26 | + | sendtext.py | |
| 27 | + | ||
| 28 | + | This script provides functionality to send text messages over a network using the VBAN protocol. | |
| 29 | + | It includes classes and functions to construct and send VBAN packets with text data, read configuration | |
| 30 | + | from a TOML file, and handle command-line arguments for customization. | |
| 31 | + | ||
| 32 | + | Classes: | |
| 33 | + | RTHeader: Represents the VBAN header for a text stream. | |
| 34 | + | RequestPacket: Encapsulates a VBAN request packet with text data. | |
| 35 | + | VbanSendText: Manages the sending of text messages over VBAN with rate limiting. | |
| 36 | + | ||
| 37 | + | Functions: | |
| 38 | + | ratelimit: Decorator to enforce a rate limit on a function. | |
| 39 | + | conn_from_toml: Reads a TOML configuration file and returns its contents as a dictionary. | |
| 40 | + | parse_args: Parses command-line arguments. | |
| 41 | + | main: Main function to send text using VbanSendText. | |
| 42 | + | ||
| 43 | + | Usage: | |
| 44 | + | Run the script with appropriate command-line arguments to send text messages. | |
| 45 | + | Example: | |
| 46 | + | python sendtext.py --config /path/to/config.toml --log-level DEBUG "strip[0].mute=0 strip[1].mute=0" | |
| 47 | + | """ | |
| 48 | + | ||
| 25 | 49 | import argparse | |
| 26 | 50 | import functools | |
| 27 | 51 | import logging | |
| @@ -43,7 +67,7 @@ class RTHeader: | |||
| 43 | 67 | bps: int | |
| 44 | 68 | channel: int | |
| 45 | 69 | VBAN_PROTOCOL_TXT = 0x40 | |
| 46 | - | framecounter: bytes = (0).to_bytes(4, 'little') | |
| 70 | + | framecounter: bytes = (0).to_bytes(4, "little") | |
| 47 | 71 | # fmt: off | |
| 48 | 72 | BPS_OPTS: list[int] = field(default_factory=lambda: [ | |
| 49 | 73 | 0, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31250, | |
| @@ -60,22 +84,22 @@ class RTHeader: | |||
| 60 | 84 | try: | |
| 61 | 85 | self.bps_index = self.BPS_OPTS.index(self.bps) | |
| 62 | 86 | except ValueError as e: | |
| 63 | - | ERR_MSG = f'Invalid bps: {self.bps}, must be one of {self.BPS_OPTS}' | |
| 87 | + | ERR_MSG = f"Invalid bps: {self.bps}, must be one of {self.BPS_OPTS}" | |
| 64 | 88 | e.add_note(ERR_MSG) | |
| 65 | 89 | raise | |
| 66 | 90 | ||
| 67 | 91 | def __sr(self) -> bytes: | |
| 68 | - | return (RTHeader.VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, 'little') | |
| 92 | + | return (RTHeader.VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, "little") | |
| 69 | 93 | ||
| 70 | 94 | def __nbc(self) -> bytes: | |
| 71 | - | return (self.channel).to_bytes(1, 'little') | |
| 95 | + | return (self.channel).to_bytes(1, "little") | |
| 72 | 96 | ||
| 73 | 97 | def build(self) -> bytes: | |
| 74 | - | header = 'VBAN'.encode('utf-8') | |
| 98 | + | header = "VBAN".encode("utf-8") | |
| 75 | 99 | header += self.__sr() | |
| 76 | - | header += (0).to_bytes(1, 'little') | |
| 100 | + | header += (0).to_bytes(1, "little") | |
| 77 | 101 | header += self.__nbc() | |
| 78 | - | header += (0x10).to_bytes(1, 'little') | |
| 102 | + | header += (0x10).to_bytes(1, "little") | |
| 79 | 103 | header += self.name.encode() + bytes(16 - len(self.name)) | |
| 80 | 104 | header += RTHeader.framecounter | |
| 81 | 105 | return header | |
| @@ -86,15 +110,15 @@ class RequestPacket: | |||
| 86 | 110 | self.header = header | |
| 87 | 111 | ||
| 88 | 112 | def encode(self, text: str) -> bytes: | |
| 89 | - | return self.header.build() + text.encode('utf-8') | |
| 113 | + | return self.header.build() + text.encode("utf-8") | |
| 90 | 114 | ||
| 91 | 115 | def bump_framecounter(self) -> None: | |
| 92 | 116 | self.header.framecounter = ( | |
| 93 | - | int.from_bytes(self.header.framecounter, 'little') + 1 | |
| 94 | - | ).to_bytes(4, 'little') | |
| 117 | + | int.from_bytes(self.header.framecounter, "little") + 1 | |
| 118 | + | ).to_bytes(4, "little") | |
| 95 | 119 | ||
| 96 | 120 | logger.debug( | |
| 97 | - | f'framecounter: {int.from_bytes(self.header.framecounter, "little")}' | |
| 121 | + | f"framecounter: {int.from_bytes(self.header.framecounter, 'little')}" | |
| 98 | 122 | ) | |
| 99 | 123 | ||
| 100 | 124 | ||
| @@ -129,12 +153,12 @@ def ratelimit(func: Callable) -> Callable: | |||
| 129 | 153 | class VbanSendText: | |
| 130 | 154 | def __init__(self, **kwargs): | |
| 131 | 155 | defaultkwargs = { | |
| 132 | - | 'host': 'localhost', | |
| 133 | - | 'port': 6980, | |
| 134 | - | 'streamname': 'Command1', | |
| 135 | - | 'bps': 256000, | |
| 136 | - | 'channel': 0, | |
| 137 | - | 'delay': 0.02, | |
| 156 | + | "host": "localhost", | |
| 157 | + | "port": 6980, | |
| 158 | + | "streamname": "Command1", | |
| 159 | + | "bps": 256000, | |
| 160 | + | "channel": 0, | |
| 161 | + | "delay": 0.02, | |
| 138 | 162 | } | |
| 139 | 163 | defaultkwargs.update(kwargs) | |
| 140 | 164 | self.__dict__.update(defaultkwargs) | |
| @@ -161,7 +185,7 @@ class VbanSendText: | |||
| 161 | 185 | self._request.bump_framecounter() | |
| 162 | 186 | ||
| 163 | 187 | ||
| 164 | - | def conn_from_toml(filepath: str = 'config.toml') -> dict: | |
| 188 | + | def conn_from_toml(filepath: str = "config.toml") -> dict: | |
| 165 | 189 | """ | |
| 166 | 190 | Reads a TOML configuration file and returns its contents as a dictionary. | |
| 167 | 191 | Args: | |
| @@ -178,15 +202,15 @@ def conn_from_toml(filepath: str = 'config.toml') -> dict: | |||
| 178 | 202 | pn = Path(filepath) | |
| 179 | 203 | if not pn.exists(): | |
| 180 | 204 | logger.info( | |
| 181 | - | f'no {pn} found, using defaults: localhost:6980 streamname: Command1' | |
| 205 | + | f"no {pn} found, using defaults: localhost:6980 streamname: Command1" | |
| 182 | 206 | ) | |
| 183 | 207 | return {} | |
| 184 | 208 | ||
| 185 | 209 | try: | |
| 186 | - | with open(pn, 'rb') as f: | |
| 210 | + | with open(pn, "rb") as f: | |
| 187 | 211 | return tomllib.load(f) | |
| 188 | 212 | except tomllib.TOMLDecodeError as e: | |
| 189 | - | raise ValueError(f'Error decoding TOML file: {e}') from e | |
| 213 | + | raise ValueError(f"Error decoding TOML file: {e}") from e | |
| 190 | 214 | ||
| 191 | 215 | ||
| 192 | 216 | def parse_args() -> argparse.Namespace: | |
| @@ -201,24 +225,24 @@ def parse_args() -> argparse.Namespace: | |||
| 201 | 225 | text (str, optional): Text to send. | |
| 202 | 226 | """ | |
| 203 | 227 | ||
| 204 | - | parser = argparse.ArgumentParser(description='Voicemeeter VBAN Send Text CLI') | |
| 228 | + | parser = argparse.ArgumentParser(description="Voicemeeter VBAN Send Text CLI") | |
| 205 | 229 | parser.add_argument( | |
| 206 | - | '--log-level', | |
| 230 | + | "--log-level", | |
| 207 | 231 | type=str, | |
| 208 | - | choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], | |
| 209 | - | default='INFO', | |
| 210 | - | help='Set the logging level', | |
| 232 | + | choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], | |
| 233 | + | default="INFO", | |
| 234 | + | help="Set the logging level", | |
| 211 | 235 | ) | |
| 212 | 236 | parser.add_argument( | |
| 213 | - | '--config', type=str, default='config.toml', help='Path to config file' | |
| 237 | + | "--config", type=str, default="config.toml", help="Path to config file" | |
| 214 | 238 | ) | |
| 215 | 239 | parser.add_argument( | |
| 216 | - | '-i', | |
| 217 | - | '--input-file', | |
| 218 | - | type=argparse.FileType('r'), | |
| 240 | + | "-i", | |
| 241 | + | "--input-file", | |
| 242 | + | type=argparse.FileType("r"), | |
| 219 | 243 | default=sys.stdin, | |
| 220 | 244 | ) | |
| 221 | - | parser.add_argument('text', nargs='?', type=str, help='Text to send') | |
| 245 | + | parser.add_argument("text", nargs="?", type=str, help="Text to send") | |
| 222 | 246 | return parser.parse_args() | |
| 223 | 247 | ||
| 224 | 248 | ||
| @@ -241,14 +265,14 @@ def main(config: dict): | |||
| 241 | 265 | ||
| 242 | 266 | for line in args.input_file: | |
| 243 | 267 | line = line.strip() | |
| 244 | - | if line.upper() == 'Q': | |
| 268 | + | if line.upper() == "Q": | |
| 245 | 269 | break | |
| 246 | 270 | ||
| 247 | - | logger.debug(f'Sending {line}') | |
| 271 | + | logger.debug(f"Sending {line}") | |
| 248 | 272 | vban.sendtext(line) | |
| 249 | 273 | ||
| 250 | 274 | ||
| 251 | - | if __name__ == '__main__': | |
| 275 | + | if __name__ == "__main__": | |
| 252 | 276 | args = parse_args() | |
| 253 | 277 | ||
| 254 | 278 | logging.basicConfig(level=args.log_level) | |
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 22 insertions, 15 deletions
sendtext.py
| @@ -28,7 +28,7 @@ import logging | |||
| 28 | 28 | import socket | |
| 29 | 29 | import sys | |
| 30 | 30 | import time | |
| 31 | - | from dataclasses import dataclass | |
| 31 | + | from dataclasses import dataclass, field | |
| 32 | 32 | from pathlib import Path | |
| 33 | 33 | from typing import Callable | |
| 34 | 34 | ||
| @@ -40,10 +40,29 @@ logger = logging.getLogger(__name__) | |||
| 40 | 40 | @dataclass | |
| 41 | 41 | class RTHeader: | |
| 42 | 42 | name: str | |
| 43 | - | bps_index: int | |
| 43 | + | bps: int | |
| 44 | 44 | channel: int | |
| 45 | 45 | VBAN_PROTOCOL_TXT = 0x40 | |
| 46 | 46 | framecounter: bytes = (0).to_bytes(4, 'little') | |
| 47 | + | # fmt: off | |
| 48 | + | BPS_OPTS: list[int] = field(default_factory=lambda: [ | |
| 49 | + | 0, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31250, | |
| 50 | + | 38400, 57600, 115200, 128000, 230400, 250000, 256000, 460800, 921600, | |
| 51 | + | 1000000, 1500000, 2000000, 3000000 | |
| 52 | + | ]) | |
| 53 | + | # fmt: on | |
| 54 | + | ||
| 55 | + | def __post_init__(self): | |
| 56 | + | if len(self.name) > 16: | |
| 57 | + | raise ValueError( | |
| 58 | + | f"Stream name got: '{self.name}', want: must be 16 characters or fewer" | |
| 59 | + | ) | |
| 60 | + | try: | |
| 61 | + | self.bps_index = self.BPS_OPTS.index(self.bps) | |
| 62 | + | except ValueError as e: | |
| 63 | + | ERR_MSG = f'Invalid bps: {self.bps}, must be one of {self.BPS_OPTS}' | |
| 64 | + | e.add_note(ERR_MSG) | |
| 65 | + | raise | |
| 47 | 66 | ||
| 48 | 67 | def __sr(self) -> bytes: | |
| 49 | 68 | return (RTHeader.VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, 'little') | |
| @@ -108,14 +127,6 @@ def ratelimit(func: Callable) -> Callable: | |||
| 108 | 127 | ||
| 109 | 128 | ||
| 110 | 129 | class VbanSendText: | |
| 111 | - | # fmt: off | |
| 112 | - | BPS_OPTS = [ | |
| 113 | - | 0, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31250, | |
| 114 | - | 38400, 57600, 115200, 128000, 230400, 250000, 256000, 460800, 921600, | |
| 115 | - | 1000000, 1500000, 2000000, 3000000 | |
| 116 | - | ] | |
| 117 | - | # fmt: on | |
| 118 | - | ||
| 119 | 130 | def __init__(self, **kwargs): | |
| 120 | 131 | defaultkwargs = { | |
| 121 | 132 | 'host': 'localhost', | |
| @@ -127,11 +138,7 @@ class VbanSendText: | |||
| 127 | 138 | } | |
| 128 | 139 | defaultkwargs.update(kwargs) | |
| 129 | 140 | self.__dict__.update(defaultkwargs) | |
| 130 | - | self._request = RequestPacket( | |
| 131 | - | RTHeader( | |
| 132 | - | self.streamname, VbanSendText.BPS_OPTS.index(self.bps), self.channel | |
| 133 | - | ) | |
| 134 | - | ) | |
| 141 | + | self._request = RequestPacket(RTHeader(self.streamname, self.bps, self.channel)) | |
| 135 | 142 | self.lastsent = 0 | |
| 136 | 143 | ||
| 137 | 144 | def __enter__(self): | |
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 2 insertions
sendtext.py
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | # SOFTWARE. | |
| 24 | 24 | ||
| 25 | 25 | import argparse | |
| 26 | + | import functools | |
| 26 | 27 | import logging | |
| 27 | 28 | import socket | |
| 28 | 29 | import sys | |
| @@ -95,6 +96,7 @@ def ratelimit(func: Callable) -> Callable: | |||
| 95 | 96 | pass | |
| 96 | 97 | """ | |
| 97 | 98 | ||
| 99 | + | @functools.wraps(func) | |
| 98 | 100 | def wrapper(self, *args, **kwargs): | |
| 99 | 101 | now = time.time() | |
| 100 | 102 | if now - self.lastsent < self.delay: | |
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 12 insertions, 2 deletions
sendtext.py
| @@ -106,19 +106,29 @@ def ratelimit(func: Callable) -> Callable: | |||
| 106 | 106 | ||
| 107 | 107 | ||
| 108 | 108 | class VbanSendText: | |
| 109 | + | # fmt: off | |
| 110 | + | BPS_OPTS = [ | |
| 111 | + | 0, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31250, | |
| 112 | + | 38400, 57600, 115200, 128000, 230400, 250000, 256000, 460800, 921600, | |
| 113 | + | 1000000, 1500000, 2000000, 3000000 | |
| 114 | + | ] | |
| 115 | + | # fmt: on | |
| 116 | + | ||
| 109 | 117 | def __init__(self, **kwargs): | |
| 110 | 118 | defaultkwargs = { | |
| 111 | 119 | 'host': 'localhost', | |
| 112 | 120 | 'port': 6980, | |
| 113 | 121 | 'streamname': 'Command1', | |
| 114 | - | 'bps_index': 0, | |
| 122 | + | 'bps': 256000, | |
| 115 | 123 | 'channel': 0, | |
| 116 | 124 | 'delay': 0.02, | |
| 117 | 125 | } | |
| 118 | 126 | defaultkwargs.update(kwargs) | |
| 119 | 127 | self.__dict__.update(defaultkwargs) | |
| 120 | 128 | self._request = RequestPacket( | |
| 121 | - | RTHeader(self.streamname, self.bps_index, self.channel) | |
| 129 | + | RTHeader( | |
| 130 | + | self.streamname, VbanSendText.BPS_OPTS.index(self.bps), self.channel | |
| 131 | + | ) | |
| 122 | 132 | ) | |
| 123 | 133 | self.lastsent = 0 | |
| 124 | 134 | ||
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 1 insertion, 1 deletion
sendtext.py
| @@ -182,7 +182,7 @@ def parse_args() -> argparse.Namespace: | |||
| 182 | 182 | text (str, optional): Text to send. | |
| 183 | 183 | """ | |
| 184 | 184 | ||
| 185 | - | parser = argparse.ArgumentParser(description='Send text to VBAN') | |
| 185 | + | parser = argparse.ArgumentParser(description='Voicemeeter VBAN Send Text CLI') | |
| 186 | 186 | parser.add_argument( | |
| 187 | 187 | '--log-level', | |
| 188 | 188 | type=str, | |
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 33 insertions, 33 deletions
sendtext.py
| @@ -42,20 +42,20 @@ class RTHeader: | |||
| 42 | 42 | bps_index: int | |
| 43 | 43 | channel: int | |
| 44 | 44 | VBAN_PROTOCOL_TXT = 0x40 | |
| 45 | - | framecounter: bytes = (0).to_bytes(4, "little") | |
| 45 | + | framecounter: bytes = (0).to_bytes(4, 'little') | |
| 46 | 46 | ||
| 47 | 47 | def __sr(self) -> bytes: | |
| 48 | - | return (RTHeader.VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, "little") | |
| 48 | + | return (RTHeader.VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, 'little') | |
| 49 | 49 | ||
| 50 | 50 | def __nbc(self) -> bytes: | |
| 51 | - | return (self.channel).to_bytes(1, "little") | |
| 51 | + | return (self.channel).to_bytes(1, 'little') | |
| 52 | 52 | ||
| 53 | 53 | def build(self) -> bytes: | |
| 54 | - | header = "VBAN".encode("utf-8") | |
| 54 | + | header = 'VBAN'.encode('utf-8') | |
| 55 | 55 | header += self.__sr() | |
| 56 | - | header += (0).to_bytes(1, "little") | |
| 56 | + | header += (0).to_bytes(1, 'little') | |
| 57 | 57 | header += self.__nbc() | |
| 58 | - | header += (0x10).to_bytes(1, "little") | |
| 58 | + | header += (0x10).to_bytes(1, 'little') | |
| 59 | 59 | header += self.name.encode() + bytes(16 - len(self.name)) | |
| 60 | 60 | header += RTHeader.framecounter | |
| 61 | 61 | return header | |
| @@ -66,15 +66,15 @@ class RequestPacket: | |||
| 66 | 66 | self.header = header | |
| 67 | 67 | ||
| 68 | 68 | def encode(self, text: str) -> bytes: | |
| 69 | - | return self.header.build() + text.encode("utf-8") | |
| 69 | + | return self.header.build() + text.encode('utf-8') | |
| 70 | 70 | ||
| 71 | 71 | def bump_framecounter(self) -> None: | |
| 72 | 72 | self.header.framecounter = ( | |
| 73 | - | int.from_bytes(self.header.framecounter, "little") + 1 | |
| 74 | - | ).to_bytes(4, "little") | |
| 73 | + | int.from_bytes(self.header.framecounter, 'little') + 1 | |
| 74 | + | ).to_bytes(4, 'little') | |
| 75 | 75 | ||
| 76 | 76 | logger.debug( | |
| 77 | - | f"framecounter: {int.from_bytes(self.header.framecounter, 'little')}" | |
| 77 | + | f'framecounter: {int.from_bytes(self.header.framecounter, "little")}' | |
| 78 | 78 | ) | |
| 79 | 79 | ||
| 80 | 80 | ||
| @@ -108,12 +108,12 @@ def ratelimit(func: Callable) -> Callable: | |||
| 108 | 108 | class VbanSendText: | |
| 109 | 109 | def __init__(self, **kwargs): | |
| 110 | 110 | defaultkwargs = { | |
| 111 | - | "host": "localhost", | |
| 112 | - | "port": 6980, | |
| 113 | - | "streamname": "Command1", | |
| 114 | - | "bps_index": 0, | |
| 115 | - | "channel": 0, | |
| 116 | - | "delay": 0.02, | |
| 111 | + | 'host': 'localhost', | |
| 112 | + | 'port': 6980, | |
| 113 | + | 'streamname': 'Command1', | |
| 114 | + | 'bps_index': 0, | |
| 115 | + | 'channel': 0, | |
| 116 | + | 'delay': 0.02, | |
| 117 | 117 | } | |
| 118 | 118 | defaultkwargs.update(kwargs) | |
| 119 | 119 | self.__dict__.update(defaultkwargs) | |
| @@ -142,7 +142,7 @@ class VbanSendText: | |||
| 142 | 142 | self._request.bump_framecounter() | |
| 143 | 143 | ||
| 144 | 144 | ||
| 145 | - | def conn_from_toml(filepath: str = "config.toml") -> dict: | |
| 145 | + | def conn_from_toml(filepath: str = 'config.toml') -> dict: | |
| 146 | 146 | """ | |
| 147 | 147 | Reads a TOML configuration file and returns its contents as a dictionary. | |
| 148 | 148 | Args: | |
| @@ -159,15 +159,15 @@ def conn_from_toml(filepath: str = "config.toml") -> dict: | |||
| 159 | 159 | pn = Path(filepath) | |
| 160 | 160 | if not pn.exists(): | |
| 161 | 161 | logger.info( | |
| 162 | - | f"no {pn} found, using defaults: localhost:6980 streamname: Command1" | |
| 162 | + | f'no {pn} found, using defaults: localhost:6980 streamname: Command1' | |
| 163 | 163 | ) | |
| 164 | 164 | return {} | |
| 165 | 165 | ||
| 166 | 166 | try: | |
| 167 | - | with open(pn, "rb") as f: | |
| 167 | + | with open(pn, 'rb') as f: | |
| 168 | 168 | return tomllib.load(f) | |
| 169 | 169 | except tomllib.TOMLDecodeError as e: | |
| 170 | - | raise ValueError(f"Error decoding TOML file: {e}") from e | |
| 170 | + | raise ValueError(f'Error decoding TOML file: {e}') from e | |
| 171 | 171 | ||
| 172 | 172 | ||
| 173 | 173 | def parse_args() -> argparse.Namespace: | |
| @@ -182,24 +182,24 @@ def parse_args() -> argparse.Namespace: | |||
| 182 | 182 | text (str, optional): Text to send. | |
| 183 | 183 | """ | |
| 184 | 184 | ||
| 185 | - | parser = argparse.ArgumentParser(description="Send text to VBAN") | |
| 185 | + | parser = argparse.ArgumentParser(description='Send text to VBAN') | |
| 186 | 186 | parser.add_argument( | |
| 187 | - | "--log-level", | |
| 187 | + | '--log-level', | |
| 188 | 188 | type=str, | |
| 189 | - | choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], | |
| 190 | - | default="INFO", | |
| 191 | - | help="Set the logging level", | |
| 189 | + | choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], | |
| 190 | + | default='INFO', | |
| 191 | + | help='Set the logging level', | |
| 192 | 192 | ) | |
| 193 | 193 | parser.add_argument( | |
| 194 | - | "--config", type=str, default="config.toml", help="Path to config file" | |
| 194 | + | '--config', type=str, default='config.toml', help='Path to config file' | |
| 195 | 195 | ) | |
| 196 | 196 | parser.add_argument( | |
| 197 | - | "-i", | |
| 198 | - | "--input-file", | |
| 199 | - | type=argparse.FileType("r"), | |
| 197 | + | '-i', | |
| 198 | + | '--input-file', | |
| 199 | + | type=argparse.FileType('r'), | |
| 200 | 200 | default=sys.stdin, | |
| 201 | 201 | ) | |
| 202 | - | parser.add_argument("text", nargs="?", type=str, help="Text to send") | |
| 202 | + | parser.add_argument('text', nargs='?', type=str, help='Text to send') | |
| 203 | 203 | return parser.parse_args() | |
| 204 | 204 | ||
| 205 | 205 | ||
| @@ -222,14 +222,14 @@ def main(config: dict): | |||
| 222 | 222 | ||
| 223 | 223 | for line in args.input_file: | |
| 224 | 224 | line = line.strip() | |
| 225 | - | if line.upper() == "Q": | |
| 225 | + | if line.upper() == 'Q': | |
| 226 | 226 | break | |
| 227 | 227 | ||
| 228 | - | logger.debug(f"Sending {line}") | |
| 228 | + | logger.debug(f'Sending {line}') | |
| 229 | 229 | vban.sendtext(line) | |
| 230 | 230 | ||
| 231 | 231 | ||
| 232 | - | if __name__ == "__main__": | |
| 232 | + | if __name__ == '__main__': | |
| 233 | 233 | args = parse_args() | |
| 234 | 234 | ||
| 235 | 235 | logging.basicConfig(level=args.log_level) | |
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 3 insertions, 3 deletions
sendtext.py
| @@ -44,10 +44,10 @@ class RTHeader: | |||
| 44 | 44 | VBAN_PROTOCOL_TXT = 0x40 | |
| 45 | 45 | framecounter: bytes = (0).to_bytes(4, "little") | |
| 46 | 46 | ||
| 47 | - | def __sr(self): | |
| 47 | + | def __sr(self) -> bytes: | |
| 48 | 48 | return (RTHeader.VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, "little") | |
| 49 | 49 | ||
| 50 | - | def __nbc(self): | |
| 50 | + | def __nbc(self) -> bytes: | |
| 51 | 51 | return (self.channel).to_bytes(1, "little") | |
| 52 | 52 | ||
| 53 | 53 | def build(self) -> bytes: | |
| @@ -68,7 +68,7 @@ class RequestPacket: | |||
| 68 | 68 | def encode(self, text: str) -> bytes: | |
| 69 | 69 | return self.header.build() + text.encode("utf-8") | |
| 70 | 70 | ||
| 71 | - | def bump_framecounter(self): | |
| 71 | + | def bump_framecounter(self) -> None: | |
| 72 | 72 | self.header.framecounter = ( | |
| 73 | 73 | int.from_bytes(self.header.framecounter, "little") + 1 | |
| 74 | 74 | ).to_bytes(4, "little") | |
onyx_online bu gisti düzenledi . Düzenlemeye git
1 file changed, 41 insertions, 7 deletions
sendtext.py
| @@ -29,6 +29,7 @@ import sys | |||
| 29 | 29 | import time | |
| 30 | 30 | from dataclasses import dataclass | |
| 31 | 31 | from pathlib import Path | |
| 32 | + | from typing import Callable | |
| 32 | 33 | ||
| 33 | 34 | import tomllib | |
| 34 | 35 | ||
| @@ -67,6 +68,42 @@ class RequestPacket: | |||
| 67 | 68 | def encode(self, text: str) -> bytes: | |
| 68 | 69 | return self.header.build() + text.encode("utf-8") | |
| 69 | 70 | ||
| 71 | + | def bump_framecounter(self): | |
| 72 | + | self.header.framecounter = ( | |
| 73 | + | int.from_bytes(self.header.framecounter, "little") + 1 | |
| 74 | + | ).to_bytes(4, "little") | |
| 75 | + | ||
| 76 | + | logger.debug( | |
| 77 | + | f"framecounter: {int.from_bytes(self.header.framecounter, 'little')}" | |
| 78 | + | ) | |
| 79 | + | ||
| 80 | + | ||
| 81 | + | def ratelimit(func: Callable) -> Callable: | |
| 82 | + | """ | |
| 83 | + | Decorator to enforce a rate limit on a function. | |
| 84 | + | This decorator ensures that the decorated function is not called more frequently | |
| 85 | + | than the specified delay. If the function is called before the delay has passed | |
| 86 | + | since the last call, it will wait for the remaining time before executing. | |
| 87 | + | Args: | |
| 88 | + | func (callable): The function to be decorated. | |
| 89 | + | Returns: | |
| 90 | + | callable: The wrapped function with rate limiting applied. | |
| 91 | + | Example: | |
| 92 | + | @ratelimit | |
| 93 | + | def send_message(self, message): | |
| 94 | + | # Function implementation | |
| 95 | + | pass | |
| 96 | + | """ | |
| 97 | + | ||
| 98 | + | def wrapper(self, *args, **kwargs): | |
| 99 | + | now = time.time() | |
| 100 | + | if now - self.lastsent < self.delay: | |
| 101 | + | time.sleep(self.delay - (now - self.lastsent)) | |
| 102 | + | self.lastsent = time.time() | |
| 103 | + | return func(self, *args, **kwargs) | |
| 104 | + | ||
| 105 | + | return wrapper | |
| 106 | + | ||
| 70 | 107 | ||
| 71 | 108 | class VbanSendText: | |
| 72 | 109 | def __init__(self, **kwargs): | |
| @@ -83,6 +120,7 @@ class VbanSendText: | |||
| 83 | 120 | self._request = RequestPacket( | |
| 84 | 121 | RTHeader(self.streamname, self.bps_index, self.channel) | |
| 85 | 122 | ) | |
| 123 | + | self.lastsent = 0 | |
| 86 | 124 | ||
| 87 | 125 | def __enter__(self): | |
| 88 | 126 | self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| @@ -91,6 +129,7 @@ class VbanSendText: | |||
| 91 | 129 | def __exit__(self, exc_type, exc_value, traceback): | |
| 92 | 130 | self._sock.close() | |
| 93 | 131 | ||
| 132 | + | @ratelimit | |
| 94 | 133 | def sendtext(self, text: str): | |
| 95 | 134 | """ | |
| 96 | 135 | Sends a text message to the specified host and port. | |
| @@ -99,13 +138,8 @@ class VbanSendText: | |||
| 99 | 138 | """ | |
| 100 | 139 | ||
| 101 | 140 | self._sock.sendto(self._request.encode(text), (self.host, self.port)) | |
| 102 | - | self._request.header.framecounter = ( | |
| 103 | - | int.from_bytes(self._request.header.framecounter, "little") + 1 | |
| 104 | - | ).to_bytes(4, "little") | |
| 105 | - | logger.debug( | |
| 106 | - | f"framecounter: {int.from_bytes(self._request.header.framecounter, 'little')}" | |
| 107 | - | ) | |
| 108 | - | time.sleep(self.delay) | |
| 141 | + | ||
| 142 | + | self._request.bump_framecounter() | |
| 109 | 143 | ||
| 110 | 144 | ||
| 111 | 145 | def conn_from_toml(filepath: str = "config.toml") -> dict: | |