mirror of
https://github.com/thelounge/thelounge.git
synced 2024-06-10 17:52:25 +02:00
Create channels table in sqlite, and store channels in it
This commit is contained in:
parent
2a11c07ba9
commit
d7ed8a38a3
119
src/client.js
119
src/client.js
|
@ -141,10 +141,23 @@ function Client(manager, name, config = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.createChannel = function (attr) {
|
Client.prototype.createChannel = function (attr, network) {
|
||||||
const chan = new Chan(attr);
|
const chan = new Chan(attr);
|
||||||
chan.id = this.idChan++;
|
chan.id = this.idChan++;
|
||||||
|
|
||||||
|
if (!chan.isLoggable() || !network) {
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageStorage = this.messageStorage.find((s) => s.canProvideMessages());
|
||||||
|
|
||||||
|
if (messageStorage) {
|
||||||
|
messageStorage
|
||||||
|
.getChannelId(network, chan)
|
||||||
|
.then((id) => (chan.idStorage = id))
|
||||||
|
.catch((err) => log.error(`Failed to get storage channel id: ${err}`));
|
||||||
|
}
|
||||||
|
|
||||||
return chan;
|
return chan;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,54 +190,6 @@ Client.prototype.find = function (channelId) {
|
||||||
|
|
||||||
Client.prototype.connect = function (args, isStartup = false) {
|
Client.prototype.connect = function (args, isStartup = false) {
|
||||||
const client = this;
|
const client = this;
|
||||||
let channels = [];
|
|
||||||
|
|
||||||
// Get channel id for lobby before creating other channels for nicer ids
|
|
||||||
const lobbyChannelId = client.idChan++;
|
|
||||||
|
|
||||||
if (args.channels) {
|
|
||||||
let badName = false;
|
|
||||||
|
|
||||||
args.channels.forEach((chan) => {
|
|
||||||
if (!chan.name) {
|
|
||||||
badName = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channels.push(
|
|
||||||
client.createChannel({
|
|
||||||
name: chan.name,
|
|
||||||
key: chan.key || "",
|
|
||||||
type: chan.type,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (badName && client.name) {
|
|
||||||
log.warn(
|
|
||||||
"User '" +
|
|
||||||
client.name +
|
|
||||||
"' on network '" +
|
|
||||||
args.name +
|
|
||||||
"' has an invalid channel which has been ignored"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// `join` is kept for backwards compatibility when updating from versions <2.0
|
|
||||||
// also used by the "connect" window
|
|
||||||
} else if (args.join) {
|
|
||||||
channels = args.join
|
|
||||||
.replace(/,/g, " ")
|
|
||||||
.split(/\s+/g)
|
|
||||||
.map((chan) => {
|
|
||||||
if (!chan.match(/^[#&!+]/)) {
|
|
||||||
chan = `#${chan}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.createChannel({
|
|
||||||
name: chan,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const network = new Network({
|
const network = new Network({
|
||||||
uuid: args.uuid,
|
uuid: args.uuid,
|
||||||
|
@ -244,12 +209,60 @@ Client.prototype.connect = function (args, isStartup = false) {
|
||||||
saslAccount: String(args.saslAccount || ""),
|
saslAccount: String(args.saslAccount || ""),
|
||||||
saslPassword: String(args.saslPassword || ""),
|
saslPassword: String(args.saslPassword || ""),
|
||||||
commands: args.commands || [],
|
commands: args.commands || [],
|
||||||
channels: channels,
|
|
||||||
ignoreList: args.ignoreList ? args.ignoreList : [],
|
ignoreList: args.ignoreList ? args.ignoreList : [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set network lobby channel id
|
if (args.channels) {
|
||||||
network.channels[0].id = lobbyChannelId;
|
let badName = false;
|
||||||
|
|
||||||
|
args.channels.forEach((chan) => {
|
||||||
|
if (!chan.name) {
|
||||||
|
badName = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
network.channels.push(
|
||||||
|
client.createChannel(
|
||||||
|
{
|
||||||
|
name: chan.name,
|
||||||
|
key: chan.key || "",
|
||||||
|
type: chan.type,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (badName && client.name) {
|
||||||
|
log.warn(
|
||||||
|
"User '" +
|
||||||
|
client.name +
|
||||||
|
"' on network '" +
|
||||||
|
args.name +
|
||||||
|
"' has an invalid channel which has been ignored"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (args.join) {
|
||||||
|
// `join` is kept for backwards compatibility when updating from versions <2.0
|
||||||
|
// also used by the "connect" window
|
||||||
|
args.join
|
||||||
|
.replace(/,/g, " ")
|
||||||
|
.split(/\s+/g)
|
||||||
|
.forEach((chan) => {
|
||||||
|
if (!chan.match(/^[#&!+]/)) {
|
||||||
|
chan = `#${chan}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
network.channels.push(
|
||||||
|
client.createChannel(
|
||||||
|
{
|
||||||
|
name: chan,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
client.networks.push(network);
|
client.networks.push(network);
|
||||||
client.emit("network", {
|
client.emit("network", {
|
||||||
|
@ -281,7 +294,7 @@ Client.prototype.connect = function (args, isStartup = false) {
|
||||||
|
|
||||||
if (!isStartup) {
|
if (!isStartup) {
|
||||||
client.save();
|
client.save();
|
||||||
channels.forEach((channel) => channel.loadMessages(client, network));
|
network.channels.forEach((channel) => channel.loadMessages(client, network));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ Chan.State = {
|
||||||
function Chan(attr) {
|
function Chan(attr) {
|
||||||
_.defaults(this, attr, {
|
_.defaults(this, attr, {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
idStorage: 0,
|
||||||
messages: [],
|
messages: [],
|
||||||
name: "",
|
name: "",
|
||||||
key: "",
|
key: "",
|
||||||
|
@ -192,7 +193,7 @@ Chan.prototype.getFilteredClone = function (lastActiveChannel, lastMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
newChannel.totalMessages = this[prop].length;
|
newChannel.totalMessages = this[prop].length;
|
||||||
} else {
|
} else if (prop !== "idStorage") {
|
||||||
newChannel[prop] = this[prop];
|
newChannel[prop] = this[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,12 +118,15 @@ exports.input = function (network, chan, cmd, args) {
|
||||||
let newChan = network.getChannel(chanName);
|
let newChan = network.getChannel(chanName);
|
||||||
|
|
||||||
if (typeof newChan === "undefined") {
|
if (typeof newChan === "undefined") {
|
||||||
newChan = client.createChannel({
|
newChan = client.createChannel(
|
||||||
type: Chan.Type.SPECIAL,
|
{
|
||||||
special: Chan.SpecialType.IGNORELIST,
|
type: Chan.Type.SPECIAL,
|
||||||
name: chanName,
|
special: Chan.SpecialType.IGNORELIST,
|
||||||
data: ignored,
|
name: chanName,
|
||||||
});
|
data: ignored,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
client.emit("join", {
|
client.emit("join", {
|
||||||
network: network.uuid,
|
network: network.uuid,
|
||||||
chan: newChan.getFilteredClone(true),
|
chan: newChan.getFilteredClone(true),
|
||||||
|
|
|
@ -63,10 +63,13 @@ exports.input = function (network, chan, cmd, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newChan = this.createChannel({
|
const newChan = this.createChannel(
|
||||||
type: Chan.Type.QUERY,
|
{
|
||||||
name: targetName,
|
type: Chan.Type.QUERY,
|
||||||
});
|
name: targetName,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
|
|
||||||
this.emit("join", {
|
this.emit("join", {
|
||||||
network: network.uuid,
|
network: network.uuid,
|
||||||
|
|
|
@ -11,10 +11,13 @@ module.exports = function (irc, network) {
|
||||||
let chan = network.getChannel(data.channel);
|
let chan = network.getChannel(data.channel);
|
||||||
|
|
||||||
if (typeof chan === "undefined") {
|
if (typeof chan === "undefined") {
|
||||||
chan = client.createChannel({
|
chan = client.createChannel(
|
||||||
name: data.channel,
|
{
|
||||||
state: Chan.State.JOINED,
|
name: data.channel,
|
||||||
});
|
state: Chan.State.JOINED,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
|
|
||||||
client.emit("join", {
|
client.emit("join", {
|
||||||
network: network.uuid,
|
network: network.uuid,
|
||||||
|
|
|
@ -34,12 +34,15 @@ module.exports = function (irc, network) {
|
||||||
let chan = network.getChannel("Channel List");
|
let chan = network.getChannel("Channel List");
|
||||||
|
|
||||||
if (typeof chan === "undefined") {
|
if (typeof chan === "undefined") {
|
||||||
chan = client.createChannel({
|
chan = client.createChannel(
|
||||||
type: Chan.Type.SPECIAL,
|
{
|
||||||
special: Chan.SpecialType.CHANNELLIST,
|
type: Chan.Type.SPECIAL,
|
||||||
name: "Channel List",
|
special: Chan.SpecialType.CHANNELLIST,
|
||||||
data: msg,
|
name: "Channel List",
|
||||||
});
|
data: msg,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
|
|
||||||
client.emit("join", {
|
client.emit("join", {
|
||||||
network: network.uuid,
|
network: network.uuid,
|
||||||
|
|
|
@ -75,10 +75,13 @@ module.exports = function (irc, network) {
|
||||||
showInActive = true;
|
showInActive = true;
|
||||||
chan = network.channels[0];
|
chan = network.channels[0];
|
||||||
} else {
|
} else {
|
||||||
chan = client.createChannel({
|
chan = client.createChannel(
|
||||||
type: Chan.Type.QUERY,
|
{
|
||||||
name: target,
|
type: Chan.Type.QUERY,
|
||||||
});
|
name: target,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
|
|
||||||
client.emit("join", {
|
client.emit("join", {
|
||||||
network: network.uuid,
|
network: network.uuid,
|
||||||
|
|
|
@ -50,12 +50,15 @@ module.exports = function (irc, network) {
|
||||||
let chan = network.getChannel(chanName);
|
let chan = network.getChannel(chanName);
|
||||||
|
|
||||||
if (typeof chan === "undefined") {
|
if (typeof chan === "undefined") {
|
||||||
chan = client.createChannel({
|
chan = client.createChannel(
|
||||||
type: Chan.Type.SPECIAL,
|
{
|
||||||
special: type,
|
type: Chan.Type.SPECIAL,
|
||||||
name: chanName,
|
special: type,
|
||||||
data: data,
|
name: chanName,
|
||||||
});
|
data: data,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
client.emit("join", {
|
client.emit("join", {
|
||||||
network: network.uuid,
|
network: network.uuid,
|
||||||
chan: chan.getFilteredClone(true),
|
chan: chan.getFilteredClone(true),
|
||||||
|
|
|
@ -22,10 +22,13 @@ module.exports = function (irc, network) {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
chan = network.channels[0];
|
chan = network.channels[0];
|
||||||
} else {
|
} else {
|
||||||
chan = client.createChannel({
|
chan = client.createChannel(
|
||||||
type: Chan.Type.QUERY,
|
{
|
||||||
name: data.nick,
|
type: Chan.Type.QUERY,
|
||||||
});
|
name: data.nick,
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
|
|
||||||
client.emit("join", {
|
client.emit("join", {
|
||||||
shouldOpen: true,
|
shouldOpen: true,
|
||||||
|
|
|
@ -21,9 +21,12 @@ try {
|
||||||
const currentSchemaVersion = 1520239200;
|
const currentSchemaVersion = 1520239200;
|
||||||
|
|
||||||
const schema = [
|
const schema = [
|
||||||
// Schema version #1
|
// Tables
|
||||||
"CREATE TABLE IF NOT EXISTS options (name TEXT, value TEXT, CONSTRAINT name_unique UNIQUE (name))",
|
"CREATE TABLE IF NOT EXISTS options (name TEXT, value TEXT, CONSTRAINT name_unique UNIQUE (name))",
|
||||||
|
"CREATE TABLE IF NOT EXISTS channels (channel_id INTEGER PRIMARY KEY, network TEXT, channel TEXT, CONSTRAINT unique_channel UNIQUE (network, channel))",
|
||||||
"CREATE TABLE IF NOT EXISTS messages (network TEXT, channel TEXT, time INTEGER, type TEXT, msg TEXT)",
|
"CREATE TABLE IF NOT EXISTS messages (network TEXT, channel TEXT, time INTEGER, type TEXT, msg TEXT)",
|
||||||
|
|
||||||
|
// Indexes
|
||||||
"CREATE INDEX IF NOT EXISTS network_channel ON messages (network, channel)",
|
"CREATE INDEX IF NOT EXISTS network_channel ON messages (network, channel)",
|
||||||
"CREATE INDEX IF NOT EXISTS time ON messages (time)",
|
"CREATE INDEX IF NOT EXISTS time ON messages (time)",
|
||||||
];
|
];
|
||||||
|
@ -116,6 +119,13 @@ class MessageStorage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a new message in specified channel
|
||||||
|
*
|
||||||
|
* @param Network network - Network object where the channel is
|
||||||
|
* @param Chan channel - Channel object
|
||||||
|
* @param Msg msg - Message object to store
|
||||||
|
*/
|
||||||
index(network, channel, msg) {
|
index(network, channel, msg) {
|
||||||
if (!this.isEnabled) {
|
if (!this.isEnabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -144,6 +154,12 @@ class MessageStorage {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete stored all stored messages in a channel
|
||||||
|
*
|
||||||
|
* @param Network network - Network object where the channel is
|
||||||
|
* @param Chan channel - Channel object
|
||||||
|
*/
|
||||||
deleteChannel(network, channel) {
|
deleteChannel(network, channel) {
|
||||||
if (!this.isEnabled) {
|
if (!this.isEnabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -158,6 +174,51 @@ class MessageStorage {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the stored channel id, creates one if does not exist.
|
||||||
|
*
|
||||||
|
* @param Network network - Network object where the channel is
|
||||||
|
* @param Chan channel - Channel object
|
||||||
|
*/
|
||||||
|
getChannelId(network, channel) {
|
||||||
|
if (!this.isEnabled) {
|
||||||
|
return Promise.resolve(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelName = channel.name.toLowerCase();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.database.serialize(() =>
|
||||||
|
this.database.get(
|
||||||
|
"SELECT channel_id FROM channels WHERE network = ? AND channel = ?",
|
||||||
|
[network.uuid, channelName],
|
||||||
|
(err, row) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row) {
|
||||||
|
return resolve(row.channel_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This channel was not found, create it and "recursively" call getChannelId again
|
||||||
|
this.database.run(
|
||||||
|
"INSERT INTO channels (network, channel) VALUES (?, ?)",
|
||||||
|
[network.uuid, channelName],
|
||||||
|
(err2) => {
|
||||||
|
if (err2) {
|
||||||
|
return reject(err2);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getChannelId(network, channel).then(resolve).catch(reject);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load messages for given channel on a given network and resolve a promise with loaded messages.
|
* Load messages for given channel on a given network and resolve a promise with loaded messages.
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,9 +18,10 @@ module.exports = class PublicClient {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Object} attributes
|
* @param {Object} attributes
|
||||||
|
* @param {Network} network
|
||||||
*/
|
*/
|
||||||
createChannel(attributes) {
|
createChannel(attributes, network) {
|
||||||
return this.client.createChannel(attributes);
|
return this.client.createChannel(attributes, network);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -66,6 +66,12 @@ describe("SQLite Message Storage", function () {
|
||||||
sql:
|
sql:
|
||||||
"CREATE TABLE options (name TEXT, value TEXT, CONSTRAINT name_unique UNIQUE (name))",
|
"CREATE TABLE options (name TEXT, value TEXT, CONSTRAINT name_unique UNIQUE (name))",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "channels",
|
||||||
|
tbl_name: "channels",
|
||||||
|
sql:
|
||||||
|
"CREATE TABLE channels (channel_id INTEGER PRIMARY KEY, network TEXT, channel TEXT, CONSTRAINT unique_channel UNIQUE (network, channel))",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "messages",
|
name: "messages",
|
||||||
tbl_name: "messages",
|
tbl_name: "messages",
|
||||||
|
@ -80,6 +86,37 @@ describe("SQLite Message Storage", function () {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create channel id if not exists", function (done) {
|
||||||
|
const network = {
|
||||||
|
uuid: "network-uuid",
|
||||||
|
};
|
||||||
|
|
||||||
|
store
|
||||||
|
.getChannelId(network, {
|
||||||
|
name: "#This-Is-Channel-One",
|
||||||
|
})
|
||||||
|
.then((id) => {
|
||||||
|
expect(id).to.equal(1);
|
||||||
|
|
||||||
|
store
|
||||||
|
.getChannelId(network, {
|
||||||
|
name: "#this-is-channel-ONE",
|
||||||
|
})
|
||||||
|
.then((id2) => {
|
||||||
|
expect(id2).to.equal(1);
|
||||||
|
|
||||||
|
store
|
||||||
|
.getChannelId(network, {
|
||||||
|
name: "#this-is-channel-two",
|
||||||
|
})
|
||||||
|
.then((id3) => {
|
||||||
|
expect(id3).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should insert schema version to options table", function (done) {
|
it("should insert schema version to options table", function (done) {
|
||||||
store.database.serialize(() =>
|
store.database.serialize(() =>
|
||||||
store.database.get(
|
store.database.get(
|
||||||
|
|
Loading…
Reference in a new issue