Skip to content

Commit

Permalink
Interim getting ready for release testing
Browse files Browse the repository at this point in the history
  • Loading branch information
howroyd committed Aug 26, 2023
1 parent 69d38c4 commit 510edd0
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 37 deletions.
13 changes: 0 additions & 13 deletions .bandit

This file was deleted.

18 changes: 5 additions & 13 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
{
"python.linting.flake8Enabled": true,
"python.formatting.provider": "autopep8",
"python.linting.flake8Args": [
"autopep8.args": [
"--max-line-length",
"160"
],
"flake8.args": [
"--config",
".flake8"
],
"python.linting.flake8Path": "${workspaceFolder}/.venv/bin/flake8",
"python.linting.banditPath": "${workspaceFolder}/.venv/bin/bandit",
"python.linting.banditEnabled": true,
"python.linting.banditArgs": [
"--ini",
".bandit",
"-r"
],
"python.linting.mypyEnabled": false,
"python.linting.mypyPath": "${workspaceFolder}/.venv/bin/mypy",
}
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
TODO
# TwitchIRC

This module connects to the Twitch IRC as a basic listener client. It handles the ping pong and initial connection but otherwise does not send anything to Twitch therefore does not appear in the viewer list nor can it post to chat. As such, no oauth is required, it just works out of the box.

It can join multiple channels at the same time and will report which channel a message was received in.

## Installation

Available on PyPi at https://pypi.org/project/twitchirc-drgreengiant/

```bash
pip install twitchirc_drgreengiant
```

## Typical Usage

```python
from twitchirc_drgreengiant import twitchirc

channels = frozenset(["drgreengiant", "hpxdreamer"])

with twitchirc.TwitchIrc(channels) as irc:
while True:
msg = irc.get_message(irc)

if not msg:
continue

print("Received a message:")
print(f"{msg.channel=} from {msg.username=}")
print(f"{msg.payload=}")
print()
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "twitchirc_drgreengiant"
version = "2.0.0b3"
version = "2.0.0b4"
authors = [
{ name="Simon Howroyd", email="[email protected]" },
]
Expand Down
37 changes: 28 additions & 9 deletions src/twitchirc_drgreengiant/twitchirc.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@

from . import bufferedsocket

address_tuple = tuple[str, int]
DEFAULT_SERVER: address_tuple = ("irc.chat.twitch.tv", 6667)
IRC_LOGFILE = os.getenv("IRC_LOGFILE", None)


@enum.unique
class TwitchMessageEnum(enum.Enum):
"""Message types defined by Twitch IRC server"""
"""Ref: https://dev.twitch.tv/docs/irc/example-parser"""
"""Message types defined by Twitch IRC server
Ref: https://dev.twitch.tv/docs/irc/example-parser
"""
JOIN = enum.auto()
PART = enum.auto()
NOTICE = enum.auto()
Expand Down Expand Up @@ -70,7 +73,7 @@ def match_message_type(cls, string_to_match: str) -> Self | None:

@dataclasses.dataclass(frozen=True, slots=True)
class TwitchMessageProto(Protocol):
"""Protocol for TwitchMessage"""
"""Protocol definition for TwitchMessage"""
username: str
channel: str
payload: str
Expand Down Expand Up @@ -116,7 +119,8 @@ class NoMessageException(Exception):

@dataclasses.dataclass(slots=True)
class IrcSocket:
address: tuple[str, int] = dataclasses.field(default_factory=lambda: ("irc.chat.twitch.tv", 6667))
"""Wrapper around a socket for reading and writing to the Twitch IRC server"""
address: address_tuple = dataclasses.field(default_factory=lambda: DEFAULT_SERVER)
timeout: float = 0.25
sock: bufferedsocket.BufferedSocket = dataclasses.field(init=False)

Expand Down Expand Up @@ -144,6 +148,8 @@ def write(self, string: str) -> None:


class IrcSocketManaged(IrcSocket):
"""Context manager for IrcSocket"""

def __enter__(self):
return self

Expand All @@ -153,11 +159,12 @@ def __exit__(self, exc_type, exc_value, traceback):

@dataclasses.dataclass(slots=True)
class IrcThreadArgs:
address: tuple[str, int]
"""Arguments for the IRC thread"""
address: address_tuple
timeout: float
username: str
oauth: str
channel: list[str]
channel: frozenset[str]
max_timeout: float = 5.0
queue: mp.Queue = dataclasses.field(default_factory=mp.Queue)
event: mp.Event = dataclasses.field(default_factory=mp.Event)
Expand All @@ -176,7 +183,10 @@ def _twitch_irc_thread(args: IrcThreadArgs) -> NoReturn:
try:
print("Connecting to Twitch IRC server")

with IrcSocketManaged(args.address, args.timeout) as irc, open("./logs/" + IRC_LOGFILE, "a") if IRC_LOGFILE else contextlib.nullcontext() as record_file:
with contextlib.ExitStack() as stack:
irc = stack.enter_context(IrcSocketManaged(args.address, args.timeout))
record_file = stack.enter_context(open("./logs/" + IRC_LOGFILE, "a")) if IRC_LOGFILE else None

irc.write(f'PASS {args.oauth}\r\n')
irc.write(f'NICK {args.username}\r\n')
[irc.write(f'JOIN #{channel.strip().lower()}\r\n') for channel in args.channel]
Expand Down Expand Up @@ -208,7 +218,13 @@ class TwitchIrcConnectionError(Exception):


class TwitchIrc:
def __init__(self, channel: list[str], username: str | None = None, oauth: str | None = None, timeout: float = None):
"""Context manager for connecting to Twitch IRC server"""

def __init__(self,
channel: frozenset[str],
username: str | None = None,
oauth: str | None = None,
timeout: float = None):
self._processdata = IrcThreadArgs(
address=("irc.chat.twitch.tv", 6667),
timeout=0.25,
Expand Down Expand Up @@ -251,6 +267,7 @@ def __exit__(self, exc_type, exc_value, traceback):

@staticmethod
def get_message(irc: Self, *, timeout: float = 0.1) -> TwitchMessage | None:
"""Returns a message from the IRC server, or None if no message is available"""
msg: TwitchMessage | None = None
try:
queue_msg = irc.queue.get(timeout=timeout)
Expand All @@ -263,7 +280,9 @@ def get_message(irc: Self, *, timeout: float = 0.1) -> TwitchMessage | None:


if __name__ == "__main__":
with TwitchIrc("drgreengiant") as irc:
testchannels = frozenset(["drgreengiant"])

with TwitchIrc(testchannels) as irc:
while True:
msg = TwitchMessage.from_irc_message(irc.queue.get())

Expand Down

0 comments on commit 510edd0

Please sign in to comment.