-
-
Save joepie91/053e55ff3a9b798c16f4fca2c890ae96 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const net = require("net"); | |
const tls = require("tls"); | |
const dm = require("../data-modelling"); | |
function generateTargetList(targets) { | |
Array.from(targets).map((target) => { | |
return target.getTargetName(); | |
}).join(","); | |
} | |
/* FIXME: `matches` regex */ | |
let UUID = dm.createType("UUID", dm.string()); | |
let Nickname = dm.createType("Nickname", dm.string({ | |
matches: /^[a-z0-9_`^|\[\]{}\\-]+$/i | |
})); | |
let Username = dm.createType("Username", dm.string({ | |
matches: /^[a-z0-9_-]+$/i | |
})); | |
let Realname = dm.createType("Realname", dm.string()); | |
let RealHostname = dm.createType("RealHostname", dm.string({ | |
matches: /^[a-z0-9.-]$/i | |
})); | |
let MaskedHostname = dm.createType("MaskedHostname", dm.string({ | |
matches: /^user\/.+$/ | |
})); | |
let ChannelName = dm.createType("ChannelName", dm.string({ | |
matches: /^#[^ \u0007,]$/ | |
})); | |
let MessageTarget = dm.createTrait("MessageTarget", { | |
getTargetName: dm.expectFunction([], dm.string()) | |
}); | |
let Channel = dm.createType("Channel", { | |
name: ChannelName, | |
presences: dm.setOf(UserPresence) | |
// .unique("$.user") | |
.default(() => { | |
return new Set(); | |
}), | |
addUser: dm.guard(User, function (user) { | |
this.presences.add(UserPresence({ | |
user: user, | |
isOwner: false, | |
isOperator: false, | |
hasVoice: false | |
})); | |
}), | |
removeUser: dm.guard(User, function (user) { | |
this.presences.where({ | |
user: user | |
}).delete(); | |
}) | |
}).implements(MessageTarget, { | |
getTargetName: function () { | |
return this.name; | |
} | |
}); | |
let Client = dm.createType("Client", { | |
id: UUID, | |
socket: dm.either( | |
dm.instanceOf(net.Socket), | |
dm.instanceOf(tls.Socket) | |
), | |
user: User, | |
connectedAt: dm.date(), | |
lastPong: dm.date(), | |
send: dm.guard([Message], function (message) { | |
this.socket.send(message.encode()); | |
}) | |
}); | |
let User = dm.createType("User", { | |
nickname: Nickname, | |
username: Username, | |
realname: Realname, | |
hostname: dm.either(RealHostname, MaskedHostname) | |
.validate((hostname, data) => { | |
if (data.authenticated === true) { | |
return (hostname instanceof MaskedHostname); | |
} else { | |
return (hostname instanceof RealHostname); | |
} | |
}), | |
authenticated: dm.boolean(), | |
idle: dm.boolean(), | |
clients: dm.setOf(Client), | |
joinedChannels: dm.setOf(Channel), | |
send: dm.guard([Message], function (message) { | |
this.clients.forEach((client) => { | |
client.send(message); | |
}); | |
}) | |
}).implements(MessageTarget, { | |
getTargetName: function () { | |
return this.nickname; | |
} | |
}); | |
let UserPresence = dm.createType("UserPresence", { | |
user: User, | |
isOwner: dm.boolean(), | |
isOperator: dm.boolean(), | |
hasVoice: dm.boolean() | |
}); | |
/* TODO: Allow dm.instanceOf(dm.either(A, B))? */ | |
let Server = dm.createType("Server", { | |
listeners: dm.setOf(dm.either( | |
dm.instanceOf(net.Server), | |
dm.instanceOf(tls.Server) | |
)), | |
channels: dm.mapOf(dm.string(), Channel), | |
users: dm.mapOf(dm.string(), User), | |
clients: dm.setOf(Client), | |
broadcast: dm.guard([Message], function (message) { | |
this.clients.forEach((client) => { | |
client.send(message); | |
}); | |
}) | |
}); | |
let Message = dm.createTrait("Message", { | |
encode: dm.expectFunction([], dm.string()) | |
}); | |
let NoticeMessage = dm.createType("NoticeMessage", { | |
targets: dm.setOf(MessageTarget), | |
message: dm.string() | |
}).implements(Message, { | |
encode: function () { | |
return `NOTICE ${generateTargetList(this.targets)} :${this.message}`; | |
} | |
}); | |
let TextMessage = dm.createType("TextMessage", { | |
targets: dm.setOf(MessageTarget), | |
message: dm.string() | |
}).implements(Message, { | |
encode: function () { | |
return `PRIVMSG ${generateTargetList(this.targets)} :${this.message}`; | |
} | |
}); | |
let CTCPType = dm.createType("CTCPType", dm.string({ | |
matches: /^[a-z]+$/i | |
})); | |
let CTCPMessage = dm.createType("CTCPMessage", { | |
targets: dm.setOf(MessageTarget), | |
type: CTCPType, | |
message: dm.string() | |
}).implements(Message, { | |
encode: function () { | |
return TextMessage({ | |
targets: this.targets, | |
message: `\u0001${this.type.toUpperCase()} ${this.message}\u0001` | |
}).encode(); | |
} | |
}); | |
let ActionMessage = dm.createType("ActionMessage", { | |
targets: dm.setOf(MessageTarget), | |
message: dm.string() | |
}).implements(Message, { | |
encode: function () { | |
return CTCPMessage({ | |
targets: this.targets, | |
type: "ACTION", | |
message: this.message | |
}).encode(); | |
} | |
}); | |
let CTCPResponse = dm.createType("CTCPResponse", { | |
targets: dm.setOf(MessageTarget), | |
type: CTCPType, | |
message: dm.string() | |
}).implements(Message, { | |
encode: function () { | |
return NoticeMessage({ | |
targets: this.targets, | |
message: `\u0001${this.type.toUpperCase()} ${this.message}\u0001` | |
}); | |
} | |
}); | |
module.exports = { | |
User, | |
UserPresence, | |
Channel, | |
Client, | |
Server, | |
NoticeMessage, | |
TextMessage, | |
ActionMessage | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment