MAJOR: ADDED SQLITE MSG PERSISTENCE

This commit is contained in:
meera 2026-07-16 01:24:44 +03:00
parent 44da1e17a6
commit 0898143876

148
main.go
View File

@ -7,6 +7,7 @@ package main
import (
"bufio"
"crypto/sha256"
"database/sql"
"encoding/base64"
"encoding/hex"
"encoding/json"
@ -20,6 +21,7 @@ import (
"time"
"github.com/ergochat/irc-go/ircmsg"
_ "github.com/mattn/go-sqlite3"
)
type Client struct {
@ -71,6 +73,7 @@ var (
cliMu sync.RWMutex // mutex for clients
motdString []string
motdFile string
db *sql.DB
)
func loadConfig() (*Config, error) {
@ -123,6 +126,7 @@ func main() {
if err != nil {
log.Fatalf("Error listening: %v\n", err)
}
sqlinitDB()
log.Printf("IRC server listening on port %d\n", cfg.Port)
go startHTTP(*cfg)
for {
@ -450,18 +454,20 @@ func (c *Client) serveHistory(subcmd, target string, count int, params []string)
c.fwrite(":server 461 %s CHATHISTORY :Not enough parameters\r\n", c.nick)
return
}
msgid := params[2] // format: msgid=12345
msgid = strings.TrimPrefix(msgid, "msgid=")
idx := findMsgIndex(ch.history, msgid)
if idx == -1 {
msgid := strings.TrimPrefix(params[2], "msgid=")
rows, err := db.Query(`SELECT msgid, timestamp, prefix, command, msg
FROM messages
WHERE channel=? AND msgid < ?
ORDER BY timestamp DESC LIMIT ?`,
target, msgid, count)
if err != nil {
c.fwrite(":server 718 %s %s :No history available\r\n", c.nick, target)
return
}
start := idx - count
if start < 0 {
start = 0
}
msgs = ch.history[start:idx]
defer rows.Close()
msgs = scanRows(rows, target)
case "AFTER":
if len(params) < 4 {
@ -469,16 +475,19 @@ func (c *Client) serveHistory(subcmd, target string, count int, params []string)
return
}
msgid := strings.TrimPrefix(params[2], "msgid=")
idx := findMsgIndex(ch.history, msgid)
if idx == -1 {
rows, err := db.Query(`SELECT msgid, timestamp, prefix, command, msg
FROM messages
WHERE channel=? AND msgid > ?
ORDER BY timestamp ASC LIMIT ?`,
target, msgid, count)
if err != nil {
c.fwrite(":server 718 %s %s :No history available\r\n", c.nick, target)
return
}
end := idx + 1 + count
if end > len(ch.history) {
end = len(ch.history)
}
msgs = ch.history[idx+1 : end]
defer rows.Close()
msgs = scanRows(rows, target)
case "AROUND":
if len(params) < 4 {
@ -486,21 +495,27 @@ func (c *Client) serveHistory(subcmd, target string, count int, params []string)
return
}
msgid := strings.TrimPrefix(params[2], "msgid=")
idx := findMsgIndex(ch.history, msgid)
if idx == -1 {
c.fwrite(":server 718 %s %s :No history available\r\n", c.nick, target)
return
}
half := count / 2
start := idx - half
if start < 0 {
start = 0
beforeRows, _ := db.Query(`SELECT msgid, timestamp, prefix, command, msg
FROM messages
WHERE channel=? AND msgid < ?
ORDER BY timestamp DESC LIMIT ?`,
target, msgid, half)
beforeMsgs := scanRows(beforeRows, target)
anchor, err := findMsgIndex(target, msgid)
var anchorMsg []HistoryMessage
if err == nil && anchor != nil {
anchorMsg = []HistoryMessage{*anchor}
}
end := idx + half + 1
if end > len(ch.history) {
end = len(ch.history)
}
msgs = ch.history[start:end]
afterRows, _ := db.Query(`SELECT msgid, timestamp, prefix, command, msg
FROM messages
WHERE channel=? AND msgid > ?
ORDER BY timestamp ASC LIMIT ?`,
target, msgid, half)
afterMsgs := scanRows(afterRows, target)
msgs = append(beforeMsgs, anchorMsg...)
msgs = append(msgs, afterMsgs...)
}
batchID := fmt.Sprintf("hist-%d", time.Now().UnixNano())
c.fwrite(":server BATCH +%s chathistory %s\r\n", batchID, target)
@ -519,13 +534,20 @@ func (c *Client) serveHistory(subcmd, target string, count int, params []string)
c.fwrite(":server BATCH -%s\r\n", batchID)
}
func findMsgIndex(history []HistoryMessage, msgid string) int {
for i, m := range history {
if m.MsgID == msgid {
return i
func findMsgIndex(channel, msgid string) (*HistoryMessage, error) {
row := db.QueryRow(`SELECT msgid, timestamp, prefix, command, msg
FROM messages
WHERE channel=? AND msgid=?`, channel, msgid)
var m HistoryMessage
var ts string
err := row.Scan(&m.MsgID, &ts, &m.Prefix, &m.Command, &m.Text)
if err != nil {
return nil, err
}
}
return -1
m.Time, _ = time.Parse(time.RFC3339, ts)
m.Params = []string{channel}
return &m, nil
}
func kickUser(sender *Client, channelName, targetNick, reason string) {
@ -686,6 +708,7 @@ func sendMessage(sender *Client, target, text string) {
if !ok {
return
}
ch.mu.RLock()
_, member := ch.members[sender.nick]
ch.mu.RUnlock()
if !member {
@ -795,6 +818,19 @@ func storeMessage(ch *Channel, sender *Client, target, text string) {
if len(ch.history) > 1000 {
ch.history = ch.history[1:] // drop oldest
}
_, err := db.Exec(`INSERT INTO messages(msgid, nick, timestamp, channel, prefix, command, msg)
VALUES(?, ?, ?, ?, ?, ?, ?)`,
msg.MsgID,
sender.nick,
msg.Time.UTC().Format(time.RFC3339),
target,
msg.Prefix,
msg.Command,
msg.Text,
)
if err != nil {
log.Printf("storeMessage error: %v", err)
}
}
func nicknameExists(nick string) bool {
cliMu.RLock()
@ -890,3 +926,45 @@ func checkCredentials(user, pass string) bool {
}
return false
}
// database stuff
func sqlinitDB() error {
var err error
db, err = sql.Open("sqlite3", "irc.db")
if err != nil {
return err
}
/*
msg := HistoryMessage{
MsgID: fmt.Sprintf("%d", time.Now().UnixNano()),
Time: time.Now(),
Prefix: fmt.Sprintf("%s!%s@%s", sender.nick, sender.user, sender.host),
Command: "PRIVMSG",
Params: []string{target},
Text: text,
}
*/
stmt := `CREATE TABLE IF NOT EXISTS messages(
msgid TEXT PRIMARY KEY,
nick TEXT,
timestamp TEXT,
channel TEXT,
prefix TEXT,
command TEXT,
msg TEXT
)`
_, err = db.Exec(stmt)
return err
}
func scanRows(rows *sql.Rows, channel string) []HistoryMessage {
var msgs []HistoryMessage
for rows.Next() {
var m HistoryMessage
var ts string
rows.Scan(&m.MsgID, &ts, &m.Prefix, &m.Command, &m.Text)
m.Time, _ = time.Parse(time.RFC3339, ts)
m.Params = []string{channel}
msgs = append(msgs, m)
}
return msgs
}