Compare commits

..

No commits in common. "main" and "stable" have entirely different histories.
main ... stable

3 changed files with 31 additions and 167 deletions

View file

@ -1,71 +0,0 @@
# 0.0.9 (Pre-Release)
* Multi-server support (Progress: STARTING)
* Full Chanserv support in `pawserv` plugin (Progress: PLANNED)
* Dockerfile (Progress: PLANNED)
# 0.0.7
* The first stable release of IRCat (SweeNet stability is OK)
* Implement IRC features:
* ISON
* TOPIC
* LIST (After new topic feature)
* KILL
* WEBIRC (For SweeNet webchat)
* RESTART
* Implement IRCv3 features:
* Timestamps (`server-time`)
* Account indicator (`account-tag`)
* Bare chanserv support on `pawserv` plugin
* IRC operator support
* Channel mode support
* Fixed bugs:
* Mostly SSL issues
* Erroneus nicknames/channels
* Problems with NICK not being synced
# 0.0.5
* Ident support
* Create plugins:
* Cloudflare D1 (`cfd1`)
* Implement IRC features:
* LIST (bare support)
* Rename GitServ to CatServ
* Full nickserv support on `pawserv`
* Fixed bugs:
* MOTD printed incorrectly
* Revolution IRC crashes on RPL_ISUPPORT
# 0.0.4
* Implement IRC features:
* NOTICE
* MOTD
* Create plugins:
* Botnet filtering (`botnet_protect`)
* K-Lines (`ban_engine`)
* PawServ service bots (`pawserv`)
* Create GitServ (External from `pawserv` plugin)
* SSL support
* And overall, a lot of bug fixes with the existing code.
# 0.0.1
* Create stable (Non-SSL) TCP sockets
* Add YAML config
* Create plugins:
* SQLite file (`sqlite_local`)
* Implement IRC features:
* RPL_ISUPPORT
* PING
* CAP LS (IRCv3)
* PRIVMSG
* WHO
* NAMES
* JOIN/PART
* Bare MODE support (read-only user modes `+iw` and read-only channel modes `+nt`)
* NICK
* USER
* WHOIS
* AWAY
* QUIT
* The very first version

View file

@ -1,9 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
__version__ = "0.0.9-pre" __version__ = "0.0.7"
print(f"Codename IRCat v{__version__}") print(f"Codename IRCat v{__version__}")
print("Welcome! /ᐠ ˵> ⩊ <˵マ") print("Welcome! /ᐠ ˵> ⩊ <˵マ")
import socket, time, ssl, threading, traceback, sys, subprocess, yaml, sqlite3, os, importlib, datetime import socket, time, ssl, threading, traceback, sys, subprocess, yaml, sqlite3, os, importlib, datetime
from cryptography.fernet import Fernet
from requests import get from requests import get
if not len(sys.argv) == 2: if not len(sys.argv) == 2:
print("IRCat requires the following arguments: config.yml") print("IRCat requires the following arguments: config.yml")
@ -110,16 +109,6 @@ with open(sys.argv[1], 'r') as file:
except: except:
print("IRCat needs at least one module enabled.") print("IRCat needs at least one module enabled.")
sys.exit(1) sys.exit(1)
try: multi_server = data["multiserver"]
except: multi_server = False
if multi_server:
if multi_server.__class__.__name__ != "list" and multi_server != "loner":
print("The multiserver option must be a list.")
sys.exit(1)
try: fnet = Fernet(data["fernet-key"])
except:
print("Multi-Server IRCat needs a Fernet key.")
sys.exit(1)
file.close() file.close()
print("Successfully loaded config!") print("Successfully loaded config!")
for mod in modules: for mod in modules:
@ -178,9 +167,7 @@ nickname_list = {} # Stores nicknames and the respective sockets
lower_nicks = {"catserv": "CatServ"} # Nicknames in lowercase lower_nicks = {"catserv": "CatServ"} # Nicknames in lowercase
channel_modestore = {} # Channel: {nick: mode} channel_modestore = {} # Channel: {nick: mode}
channel_modestore_identify = {} # Channel: {account: mode} channel_modestore_identify = {} # Channel: {account: mode}
property_list = {"CatServ": {"host": server, "username": "system", "realname": "Updates bot (Internal)", "modes": "o", "away": False, "external": False}} # Stores properties for active users and channels property_list = {"CatServ": {"host": "IRCatCore", "username": "Meow", "realname": "Updates bot", "modes": "iw", "away": False}} # Stores properties for active users and channels
users_externalservers = {} # Nicknames that come from different servers, useful in netsplit situations
cache_properties = {} # Cached property lists from other servers, used to send out changes
for i in mods['command']: for i in mods['command']:
requires = {} requires = {}
for j in i.__ircat_requires__: for j in i.__ircat_requires__:
@ -219,7 +206,6 @@ if ssl_option:
sockets_ssl[i].listen(1) sockets_ssl[i].listen(1)
opened=True opened=True
lower_chans = {} # Channel names in lowercase lower_chans = {} # Channel names in lowercase
sessions = {}
#def pinger(nick, connection): #def pinger(nick, connection):
# global property_list # global property_list
# while nick in property_list: # while nick in property_list:
@ -244,56 +230,6 @@ sessions = {}
# connection.shutdown(socket.SHUT_WR) # connection.shutdown(socket.SHUT_WR)
# connection.close() # connection.close()
# break # break
def multiserverpinger(sock):
while True:
time.sleep(30)
sock.sendall(fnet.encrypt(bytes("PING", "UTF-8")))
def multiserverhost(sock, client):
try:
threading.Thread(target=multiserverpinger, args=[sock]).start()
global sessions
global cache_properties
sessions[client[0]] = sock
cache_properties[client[0]] = {}
global channels_list
global property_list
global nickname_list
global topic_list
if sock.recv(2048).decode() == "meow":
sock.sendall(bytes("woem", "UTF-8"))
else:
raise Exception("This isn't an IRCat server!!")
while True:
txt = fnet.decrypt(sock.recv(2048)).decode()
if txt.split(" ")[0] == "VERIFYUSER": # When a user from a different server logs in
nck = txt.split(" ")[1] # Nickname
usr = json.loads(" ".join(txt.split(" ")[2:])) # New JSON properties
property_list[nck] = usr # Add the properties
nickname_list[nck] = {"external": client[0]} # Add the nickname indicating it's external
lower_nicks[nck.lower()] = usr # Add the lowercase nickname
users_externalservers[nck] = client[0] # Add to the list of external users
elif txt.split(" ")[0] == "SND": # When a token was sent by another server
nck = txt.split(" ")[1] # Nickname
cmd = txt.split(" ")[2] # Command
arg = txt.split(" ")[3:] # Arguments
elif txt.split(" ")[0] == "CNGPROP": # When properties changed on another server
nck = txt.split(" ")[1] # Nickname
usr = json.loads(" ".join(txt.split(" ")[2:])) # JSON
property_list[nck] = usr # Change the JSON...
cache_properties[client[0]][nck] = usr # ...And cache it
elif txt.split(" ")[0] == "CNGNICK": # When nick changed on another server
nck = txt.split(" ")[1] # Old nickname
nnck = txt.split(" ")[2] # New nickname
nickname_list[nnck] = nickname_list.pop(nck) # Move old nickname from list
property_list[nnck] = property_list.pop(nck) # Move properties...
cache_properties[client[0]][nnck] = cache_properties[client[0]].pop(nck) # ...And cache it
del lower_nicks[nck.lower()] # Remove the old lowercase nickname...
lower_nicks[nnck.lower()] = nnck # ...And replace it
elif txt == "PING":
sock.sendall(fnet.encrypt(bytes("PONG", "UTF-8")))
finally:
netsplit(client[0])
sock.close()
def session(connection, client, ip, isssl=False): def session(connection, client, ip, isssl=False):
global channels_list global channels_list
global property_list global property_list
@ -468,7 +404,7 @@ def session(connection, client, ip, isssl=False):
elif (ready and already_set) and (CAPEND if usesIRCv3 else True) and not finished: elif (ready and already_set) and (CAPEND if usesIRCv3 else True) and not finished:
print(f"User {pending} successfully logged in.") print(f"User {pending} successfully logged in.")
nickname_list[pending] = connection nickname_list[pending] = connection
property_list[pending] = {"host": hostname, "username": clident if clident != None else f"~{username }", "realname": realname, "modes": "iw", "away": False, "identified": False, "ssl": isssl, "v3cap": IRCv3Features, "last_ping": time.time(), "ping_pending": False, "pendingSend": "", "external": False} property_list[pending] = {"host": hostname, "username": clident if clident != None else f"~{username }", "realname": realname, "modes": "iw", "away": False, "identified": False, "ssl": isssl, "v3cap": IRCv3Features, "last_ping": time.time(), "ping_pending": False, "pendingSend": ""}
last_ping = time.time() last_ping = time.time()
ping_pending = False ping_pending = False
lower_nicks[pending.lower()] = pending lower_nicks[pending.lower()] = pending

57
todo.md
View file

@ -1,27 +1,28 @@
# To-Do list # To-Do list
- [ ] Implement the base of an IRCd, using Libera.Chat and RFC1459 as a reference - [ ] Implement the base of an IRCd, using Libera.Chat and RFC1459 as a reference
- [x] [Add the (full) connection process](https://mastodon.swee.codes/@swee/113659491393674897) (0.0.4) - [x] [Add the (full) connection process](https://mastodon.swee.codes/@swee/113659491393674897)
- [x] Add join/part, and it's requirements (WHO, etc) (0.0.1) - [x] Add join/part, and it's requirements (WHO, etc)
- [x] Implement proper PRIVMSG (0.0.1) - [x] Implement proper PRIVMSG
- [x] Broadcast QUIT (0.0.1) - [x] Broadcast QUIT
- [x] DNS lookup (0.0.1) - [x] DNS lookup
- [x] Identity management
- [ ] WallOps - [ ] WallOps
- [ ] Channel invite system - [ ] Channel invite system
- [ ] Wildcard logic (for +b and +q) - [ ] Wildcard logic (for +b and +q)
- [x] Send PING and wait for PONG (0.0.1) - [x] Send PING and wait for PONG
- [x] Reply PONG if received PING (0.0.1) - [x] Reply PONG if received PING
- [x] [Change of nicknames](https://mastodon.swee.codes/@swee/113642104470536887) (0.0.1) - [x] [Change of nicknames](https://mastodon.swee.codes/@swee/113642104470536887)
- [x] Away (0.0.1) - [x] Away
- [ ] Multi-server support - [ ] Multi-server support
- [x] `LIST` (0.0.5) - [x] `LIST`
- [x] `TOPIC` (0.0.7) - [x] `TOPIC`
- [ ] [Database support](https://discuss.swee.codes/t/41) - [ ] [Database support](https://discuss.swee.codes/t/41)
- [ ] User Flags - [ ] User Flags
- [ ] i (invisible) - [ ] i (invisible)
- [x] o (IRCOP) (0.0.7) - [x] o (IRCOP)
- [ ] w (gets wallops) - [ ] w (gets wallops)
- [ ] Channel Flags - [ ] Channel Flags
- [x] o \<user\> (CHANOP) (0.0.7) - [x] o \<user\> (CHANOP)
- [ ] v \<user\> (voice) - [ ] v \<user\> (voice)
- [ ] m (moderated, only let voice or op talk) - [ ] m (moderated, only let voice or op talk)
- [ ] s (don't show in LIST) - [ ] s (don't show in LIST)
@ -38,32 +39,30 @@
- [ ] Destructive features for IRCOPS - [ ] Destructive features for IRCOPS
- [ ] `KILL <user> <comment>` - [ ] `KILL <user> <comment>`
- [ ] `MODE <external user>` - [ ] `MODE <external user>`
- [x] `RESTART` (0.07) - [x] `RESTART`
- [ ] Extra commands - [ ] Extra commands
- [x] `NAMES` (0.0.1) - [x] `NAMES`
- [x] `WHOIS` (0.0.1) - [x] `WHOIS`
- [ ] `WHOWAS` - [ ] `WHOWAS`
- [ ] [Implement services.](modules/pawserv.py) - [ ] [Implement services.](modules/pawserv.py)
- [x] Nickserv (0.0.7) - [x] Nickserv
- [ ] ChanServ - [ ] ChanServ
- [x] CatServ (Outside of PawServ) (0.0.4) - [x] CatServ (Outside of PawServ)
- [x] Link `PRIVMSG *serv` to `*serv` (0.0.4) - [x] Link `PRIVMSG *serv` to `*serv`
- [x] Extra ~~(not planned)~~ features - [x] Extra ~~(not planned)~~ features
- [x] ident support (0.0.5) - [x] ident support
- [x] Authentication - [x] Authentication
- [x] Store credentials in ~~an SQLite3 file.~~ database engine (0.0.1) - [x] Store credentials in ~~an SQLite3 file.~~ database engine
- [x] Map NickServ IDENTIFY (0.0.7) - [x] Map NickServ IDENTIFY
- [x] Map PASS (0.0.7) - [x] Map PASS
- [x] SSL/TLS - [x] SSL/TLS
- [x] [Use a thread to accept connections on SSL port 6697](https://mastodon.swee.codes/@swee/113762525145710774) (0.0.1) - [x] [Use a thread to accept connections on SSL port 6697](https://mastodon.swee.codes/@swee/113762525145710774)
- [x] Automatically reload the certificate ~~if defined in config.~~ (0.0.1) - [x] Automatically reload the certificate ~~if defined in config.~~
- [ ] Add IRCv3 features. - [ ] Add IRCv3 features.
- [x] List capabilities (`CAP LS 302`) (0.0.1) - [x] List capabilities (`CAP LS 302`)
- [ ] `away-notify` - [ ] `away-notify`
- [ ] `tls` (STARTTLS) - [ ] `tls` (STARTTLS)
- [ ] `sasl` - [ ] `sasl`
- [x] `server-time` (0.0.7) - [x] `server-time`
- [ ] `account-notify`
- [x] `account-tag` (0.0.7)
- Will research later. - Will research later.
I am going to fully read [RFC 1459](https://datatracker.ietf.org/doc/html/rfc1459) soon and add each part to the TODO. I am going to fully read [RFC 1459](https://datatracker.ietf.org/doc/html/rfc1459) soon and add each part to the TODO.