diff --git a/lib/nntp/tiny/client.py b/lib/nntp/tiny/client.py index 55c0984..6612379 100644 --- a/lib/nntp/tiny/client.py +++ b/lib/nntp/tiny/client.py @@ -2,15 +2,19 @@ import re import socket import ssl import datetime +import email.utils from typing import Optional from nntp.tiny.socket import Connection from nntp.tiny.host import Host from nntp.tiny.response import Response, ResponseCode -from nntp.tiny.remote import RemoteNewsgroup from nntp.tiny.message import MessageRange +from nntp.tiny.remote import ( + RemoteException, RemoteNewsgroup, RemoteMessage +) + class ClientException(Exception): pass @@ -62,7 +66,12 @@ class Client(Connection): def request(self, *args): self.print(' '.join(args)) - return self._read_response() + response = self._read_response() + + if response.code.value >= 400: + raise RemoteException(response) + + return response def each_response_line(self): while True: @@ -88,9 +97,33 @@ class Client(Connection): parts = self.RE_SPLIT.split(line) if len(parts) != 4: - raise ClientException('Unexpected result from NEWGROUPS') + raise RemoteException('Unexpected result from NEWGROUPS') yield RemoteNewsgroup(parts[0], int(parts[1]), int(parts[2]), parts[3] == 'y') + + def each_newsgroup_message(self, newsgroup: RemoteNewsgroup, msgrange: Optional[MessageRange]): + self.request('GROUP', newsgroup.name) + self.request('OVER', str(msgrange)) + + for line in self.each_response_line(): + message = RemoteMessage() + parts = line.split('\t') + + message.id = int(parts[0]) + message.subject = parts[1] + message.sender = parts[2] + message.created_on = email.utils.parsedate_to_datetime(parts[3]) + message.message_id = parts[4] + message.references = parts[5] + message.size = int(parts[6]) + message.lines = int(parts[7]) + + for part in parts[8:]: + key, value = part.split(': ') + + message.headers[key] = value + + yield message diff --git a/lib/nntp/tiny/remote.py b/lib/nntp/tiny/remote.py index 6d16cde..a4f2d67 100644 --- a/lib/nntp/tiny/remote.py +++ b/lib/nntp/tiny/remote.py @@ -1,11 +1,14 @@ +class RemoteException(Exception): + pass + class RemoteNewsgroup(): __slots__ = 'name', 'low', 'high', 'post', def __init__(self, name: str, low: int, high: int, post: bool): - self.name = name - self.low = low - self.high = high - self.post = post + self.name: str = name + self.low: int = low + self.high: int = high + self.post: bool = post def __str__(self): return "%s %d %d %s" % ( @@ -14,3 +17,12 @@ class RemoteNewsgroup(): self.high, 'y' if self.post else 'n' ) + +class RemoteMessage(): + __slots__ = ( + 'id', 'subject', 'sender', 'created_on', 'message_id', + 'references', 'size', 'lines', 'headers' + ) + + def __init__(self): + self.headers = dict()