3 Commits

9 changed files with 49 additions and 13 deletions
Unified 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 asyncio import StreamReader, StreamWriter, Queue
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime
from logging import log, INFO from logging import log, INFO
from paircd.reply import RPL_CREATED, RPL_WELCOME, RPL_YOURHOST
from paircd.message import Message from paircd.message import Message
from typing import Set from typing import Set
@ -35,3 +37,19 @@ class Client:
def write_message(self, message: Message) -> None: def write_message(self, message: Message) -> None:
self.msg_queue.put_nowait(message.encode()) 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 = server.get_channel_by_name(channel_name)
channel.remove_client_by_nick(client.nickname) 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.username = msg.args[0]
client.realname = msg.args[3] 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.join import JoinHandler
from paircd.handler.mode import ModeHandler from paircd.handler.mode import ModeHandler
from paircd.handler.nick import NickHandler from paircd.handler.nick import NickHandler
from paircd.handler.ping import PingHandler
from paircd.handler.privmsg import PrivmsgHandler from paircd.handler.privmsg import PrivmsgHandler
from paircd.handler.user import UserHandler from paircd.handler.user import UserHandler
from paircd.handler.who import WhoHandler from paircd.handler.who import WhoHandler
@ -17,6 +18,7 @@ HANDLER_CLASSES = [
JoinHandler, JoinHandler,
ModeHandler, ModeHandler,
NickHandler, NickHandler,
PingHandler,
PrivmsgHandler, PrivmsgHandler,
UserHandler, UserHandler,
WhoHandler, 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 from paircd.message import Message
def cmd_fn(cmd: str, tmpl: str) -> Callable: 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) msg = tmpl.format(*args)
return ( return (
Message(cmd=cmd, args=[msg], prefix=prefix) Message(cmd=cmd, args=[msg], prefix=prefix)
if prefix is not None if prefix is not None
else Message(cmd=str(cmd), args=[msg])
else Message(cmd=cmd, args=[msg])
) )
return fn 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: def fn(target: str, *args: List[Any]) -> Message:
msg = f"{target} {tmpl.format(*args)}" msg = f"{target} {tmpl.format(*args)}"
return Message(cmd=str(cmd), args=[msg]) return Message(cmd=str(cmd), args=[msg])
@ -26,6 +26,7 @@ def reply_fn(cmd: int, tmpl: str) -> Callable:
# Commands # Commands
JOIN = cmd_fn("JOIN", "{0}") JOIN = cmd_fn("JOIN", "{0}")
MODE = cmd_fn("MODE", "{0} {1}") MODE = cmd_fn("MODE", "{0} {1}")
PONG = cmd_fn("PONG", ":{0}")
PRIVMSG = cmd_fn("PRIVMSG", "{0} :{1}") PRIVMSG = cmd_fn("PRIVMSG", "{0} :{1}")
# Error replies # 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") ERR_NOSUCHSERVER = reply_fn(402, "{0}: No such server")
# Command responses # 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_WHOISUSER = reply_fn(311, "{0} {1} {2} * :{3}")
RPL_ENDOFWHO = reply_fn(315, "{0} :End of /WHO list") RPL_ENDOFWHO = reply_fn(315, "{0} :End of /WHO list")
RPL_ENDOFWHOIS = reply_fn(318, "{0} :End of /WHOIS 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 dataclasses import dataclass, field
from datetime import datetime
from typing import Dict from typing import Dict
from paircd.client import Client from paircd.client import Client
@ -9,6 +10,7 @@ from paircd.channel import Channel
class Server: class Server:
clients_by_nick: Dict[str, Client] = field(default_factory=dict) clients_by_nick: Dict[str, Client] = field(default_factory=dict)
channels_by_name: Dict[str, Channel] = 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: def add_client(self, client: Client) -> None:
self.clients_by_nick[client.nickname] = client self.clients_by_nick[client.nickname] = client

+ 2
- 2
poetry.lock View File

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

+ 1
- 1
pyproject.toml View File

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

Loading…
Cancel
Save