3 Commits

9 changed files with 49 additions and 13 deletions
Split View
  1. +18
    -0
      paircd/client.py
  2. +1
    -3
      paircd/handler/nick.py
  3. +14
    -0
      paircd/handler/ping.py
  4. +1
    -3
      paircd/handler/user.py
  5. +2
    -0
      paircd/handlers.py
  6. +8
    -4
      paircd/reply.py
  7. +2
    -0
      paircd/server.py
  8. +2
    -2
      poetry.lock
  9. +1
    -1
      pyproject.toml

+ 18
- 0
paircd/client.py View File

@ -1,6 +1,8 @@
from asyncio import StreamReader, StreamWriter, Queue
from dataclasses import dataclass, field
from datetime import datetime
from logging import log, INFO
from paircd.reply import RPL_CREATED, RPL_WELCOME, RPL_YOURHOST
from paircd.message import Message
from typing import Set
@ -35,3 +37,19 @@ class Client:
def write_message(self, message: Message) -> None:
self.msg_queue.put_nowait(message.encode())
def register(self) -> None:
if self.registered:
return
if not (self.nickname and self.username and self.realname):
return
self.registered = True
self.log("registered")
self.write_message(RPL_WELCOME(self.nickname, self.id()))
self.write_message(RPL_YOURHOST(self.nickname, "localhost", "paircd-0.0.1"))
# TODO: Pull timestamp from Server instance
self.write_message(RPL_CREATED(self.nickname, datetime.utcnow()))

+ 1
- 3
paircd/handler/nick.py View File

@ -26,6 +26,4 @@ class NickHandler(CommandHandler):
channel = server.get_channel_by_name(channel_name)
channel.remove_client_by_nick(client.nickname)
if client.username and client.realname:
client.registered = True
client.log("registered")
client.register()

+ 14
- 0
paircd/handler/ping.py View File

@ -0,0 +1,14 @@
from paircd.reply import PONG
from paircd.client import Client
from paircd.command_handler import CommandHandler
from paircd.message import Message
from paircd.server import Server
class PingHandler(CommandHandler):
def __init__(self) -> None:
super().__init__("PING", 1)
async def handle(self, server: Server, client: Client, msg: Message) -> None:
token = msg.args[0]
client.write_message(PONG(token))

+ 1
- 3
paircd/handler/user.py View File

@ -18,6 +18,4 @@ class UserHandler(CommandHandler):
client.username = msg.args[0]
client.realname = msg.args[3]
if client.nickname:
client.registered = True
client.log("registered")
client.register()

+ 2
- 0
paircd/handlers.py View File

@ -9,6 +9,7 @@ from paircd.server import Server
from paircd.handler.join import JoinHandler
from paircd.handler.mode import ModeHandler
from paircd.handler.nick import NickHandler
from paircd.handler.ping import PingHandler
from paircd.handler.privmsg import PrivmsgHandler
from paircd.handler.user import UserHandler
from paircd.handler.who import WhoHandler
@ -17,6 +18,7 @@ HANDLER_CLASSES = [
JoinHandler,
ModeHandler,
NickHandler,
PingHandler,
PrivmsgHandler,
UserHandler,
WhoHandler,

+ 8
- 4
paircd/reply.py View File

@ -1,21 +1,21 @@
from typing import Any, Callable, List, Optional
from typing import Any, Callable, List, Optional, Union
from paircd.message import Message
def cmd_fn(cmd: str, tmpl: str) -> Callable:
def fn(prefix: Optional[str] = None, *args: List[Any]) -> Message:
def fn(*args: List[Any], prefix: Optional[str] = None) -> Message:
msg = tmpl.format(*args)
return (
Message(cmd=cmd, args=[msg], prefix=prefix)
if prefix is not None
else Message(cmd=str(cmd), args=[msg])
else Message(cmd=cmd, args=[msg])
)
return fn
def reply_fn(cmd: int, tmpl: str) -> Callable:
def reply_fn(cmd: Union[int, str], tmpl: str) -> Callable:
def fn(target: str, *args: List[Any]) -> Message:
msg = f"{target} {tmpl.format(*args)}"
return Message(cmd=str(cmd), args=[msg])
@ -26,6 +26,7 @@ def reply_fn(cmd: int, tmpl: str) -> Callable:
# Commands
JOIN = cmd_fn("JOIN", "{0}")
MODE = cmd_fn("MODE", "{0} {1}")
PONG = cmd_fn("PONG", ":{0}")
PRIVMSG = cmd_fn("PRIVMSG", "{0} :{1}")
# Error replies
@ -33,6 +34,9 @@ ERR_NOSUCHNICK = reply_fn(401, "{0} :No such nick/channel")
ERR_NOSUCHSERVER = reply_fn(402, "{0}: No such server")
# Command responses
RPL_WELCOME = reply_fn("001", "Welcome to the Internet Relay Network {0}")
RPL_YOURHOST = reply_fn("002", "Your host is {0}, running version {1}")
RPL_CREATED = reply_fn("003", "This server was created {0}")
RPL_WHOISUSER = reply_fn(311, "{0} {1} {2} * :{3}")
RPL_ENDOFWHO = reply_fn(315, "{0} :End of /WHO list")
RPL_ENDOFWHOIS = reply_fn(318, "{0} :End of /WHOIS list")

+ 2
- 0
paircd/server.py View File

@ -1,4 +1,5 @@
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict
from paircd.client import Client
@ -9,6 +10,7 @@ from paircd.channel import Channel
class Server:
clients_by_nick: Dict[str, Client] = field(default_factory=dict)
channels_by_name: Dict[str, Channel] = field(default_factory=dict)
create_time: datetime = field(default_factory=datetime.utcnow)
def add_client(self, client: Client) -> None:
self.clients_by_nick[client.nickname] = client

+ 2
- 2
poetry.lock View File

@ -248,8 +248,8 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "6a46752f4a24a2bba8adf3fc49cfab506ac08b4c2e0b7a844be1b36315301ccc"
python-versions = "^3.8"
content-hash = "298291104b6892022cb47c34680f925bc85fff59e768a225230ef84444acb002"
[metadata.files]
appdirs = [

+ 1
- 1
pyproject.toml View File

@ -5,7 +5,7 @@ description = ""
authors = ["Forest Belton <65484+forestbelton@users.noreply.github.com>"]
[tool.poetry.dependencies]
python = "^3.9"
python = "^3.8"
[tool.poetry.dev-dependencies]
pytest = "^5.2"

Loading…
Cancel
Save