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