SimpleCloudNotifier/scnserver/models/messagefilter.go
Mike Schwörer d21d775764
Some checks failed
Build Docker and Deploy / Run Unit-Tests (push) Failing after 10s
Build Docker and Deploy / Build Docker Container (push) Successful in 1m14s
Build Docker and Deploy / Deploy to Server (push) Has been skipped
Add ListSenderNames api route and use params.Add(..) in Filter classes
2024-09-20 20:37:55 +02:00

251 lines
8.5 KiB
Go

package models
import (
"crypto/sha512"
"encoding/hex"
"fmt"
"gogs.mikescher.com/BlackForestBytes/goext/dataext"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/mathext"
"gogs.mikescher.com/BlackForestBytes/goext/sq"
"strconv"
"strings"
"time"
)
type MessageFilter struct {
ConfirmedSubscriptionBy *UserID
SearchStringFTS *[]string
SearchStringPlain *[]string
Sender *[]UserID
ChannelNameCS *[]string // case-sensitive
ChannelNameCI *[]string // case-insensitive
ChannelID *[]ChannelID
SenderNameCS *[]string // case-sensitive
SenderNameCI *[]string // case-insensitive
HasSenderName *bool
SenderIP *[]string
TimestampCoalesce *time.Time
TimestampCoalesceAfter *time.Time
TimestampCoalesceBefore *time.Time
TimestampReal *time.Time
TimestampRealAfter *time.Time
TimestampRealBefore *time.Time
TimestampClient *time.Time
TimestampClientAfter *time.Time
TimestampClientBefore *time.Time
TitleCS *string // case-sensitive
TitleCI *string // case-insensitive
Priority *[]int
UserMessageID *[]string
OnlyDeleted bool
IncludeDeleted bool
CompatAcknowledged *bool
UsedKeyID *[]KeyTokenID
}
func (f MessageFilter) SQL() (string, string, sq.PP, error) {
joinClause := ""
if f.ConfirmedSubscriptionBy != nil {
joinClause += " LEFT JOIN subscriptions AS subs on messages.channel_id = subs.channel_id "
}
if f.SearchStringFTS != nil {
joinClause += " JOIN messages_fts AS mfts on (mfts.rowid = messages.rowid) "
}
sqlClauses := make([]string, 0)
params := sq.PP{}
if f.OnlyDeleted {
sqlClauses = append(sqlClauses, "(deleted=1)")
} else if f.IncludeDeleted {
// nothing, return all
} else {
sqlClauses = append(sqlClauses, "(deleted=0)") // default
}
if f.ConfirmedSubscriptionBy != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(subs.subscriber_user_id = :%s AND subs.confirmed = 1)", params.Add(*f.ConfirmedSubscriptionBy)))
}
if f.Sender != nil {
filter := make([]string, 0)
for _, v := range *f.Sender {
filter = append(filter, fmt.Sprintf("(sender_user_id = :%s)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.ChannelNameCI != nil {
filter := make([]string, 0)
for _, v := range *f.ChannelNameCI {
filter = append(filter, fmt.Sprintf("(messages.channel_internal_name = :%s COLLATE NOCASE)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.ChannelNameCS != nil {
filter := make([]string, 0)
for _, v := range *f.ChannelNameCS {
filter = append(filter, fmt.Sprintf("(messages.channel_internal_name = :%s COLLATE BINARY)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.ChannelID != nil {
filter := make([]string, 0)
for _, v := range *f.ChannelID {
filter = append(filter, fmt.Sprintf("(messages.channel_id = :%s)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.SenderNameCI != nil {
filter := make([]string, 0)
for _, v := range *f.SenderNameCI {
filter = append(filter, fmt.Sprintf("(sender_name = :%s COLLATE NOCASE)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "(sender_name IS NOT NULL AND ("+strings.Join(filter, " OR ")+"))")
}
if f.SenderNameCS != nil {
filter := make([]string, 0)
for _, v := range *f.SenderNameCS {
filter = append(filter, fmt.Sprintf("(sender_name = :%s COLLATE BINARY)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "(sender_name IS NOT NULL AND ("+strings.Join(filter, " OR ")+"))")
}
if f.HasSenderName != nil {
if *f.HasSenderName {
sqlClauses = append(sqlClauses, "(sender_name IS NOT NULL)")
} else {
sqlClauses = append(sqlClauses, "(sender_name IS NULL)")
}
}
if f.SenderIP != nil {
filter := make([]string, 0)
for _, v := range *f.SenderIP {
filter = append(filter, fmt.Sprintf("(sender_ip = :%s)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.TimestampCoalesce != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(COALESCE(timestamp_client, timestamp_real) = :%s)", params.Add((*f.TimestampCoalesce).UnixMilli())))
}
if f.TimestampCoalesceAfter != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(COALESCE(timestamp_client, timestamp_real) > :%s)", params.Add((*f.TimestampCoalesceAfter).UnixMilli())))
}
if f.TimestampCoalesceBefore != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(COALESCE(timestamp_client, timestamp_real) < :%s)", params.Add((*f.TimestampCoalesceBefore).UnixMilli())))
}
if f.TimestampReal != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(timestamp_real = :%s)", params.Add((*f.TimestampRealAfter).UnixMilli())))
}
if f.TimestampRealAfter != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(timestamp_real > :%s)", params.Add((*f.TimestampRealAfter).UnixMilli())))
}
if f.TimestampRealBefore != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(timestamp_real < :%s)", params.Add((*f.TimestampRealBefore).UnixMilli())))
}
if f.TimestampClient != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(timestamp_client IS NOT NULL AND timestamp_client = :%s)", params.Add((*f.TimestampClient).UnixMilli())))
}
if f.TimestampClientAfter != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(timestamp_client IS NOT NULL AND timestamp_client > :%s)", params.Add((*f.TimestampClientAfter).UnixMilli())))
}
if f.TimestampClientBefore != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(timestamp_client IS NOT NULL AND timestamp_client < :%s)", params.Add((*f.TimestampClientBefore).UnixMilli())))
}
if f.TitleCI != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(title = :%s COLLATE NOCASE)", params.Add(*f.TitleCI)))
}
if f.TitleCS != nil {
sqlClauses = append(sqlClauses, fmt.Sprintf("(title = :%s COLLATE BINARY)", params.Add(*f.TitleCI)))
}
if f.Priority != nil {
prioList := "(" + strings.Join(langext.ArrMap(*f.Priority, func(p int) string { return strconv.Itoa(p) }), ", ") + ")"
sqlClauses = append(sqlClauses, "(priority IN "+prioList+")")
}
if f.UserMessageID != nil {
filter := make([]string, 0)
for _, v := range *f.UserMessageID {
filter = append(filter, fmt.Sprintf("(usr_message_id = :%s)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "(usr_message_id IS NOT NULL AND ("+strings.Join(filter, " OR ")+"))")
}
if f.CompatAcknowledged != nil {
joinClause += " LEFT JOIN compat_acks AS filter_compatack_compat_acks on messages.message_id = filter_compatack_compat_acks.message_id "
if *f.CompatAcknowledged {
sqlClauses = append(sqlClauses, "(filter_compatack_compat_acks.message_id IS NOT NULL)")
} else {
sqlClauses = append(sqlClauses, "(filter_compatack_compat_acks.message_id IS NULL)")
}
}
if f.UsedKeyID != nil {
filter := make([]string, 0)
for _, v := range *f.UsedKeyID {
filter = append(filter, fmt.Sprintf("(used_key_id = :%s)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.SearchStringFTS != nil {
filter := make([]string, 0)
for _, v := range *f.SearchStringFTS {
filter = append(filter, fmt.Sprintf("(messages_fts match :%s)", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
if f.SearchStringPlain != nil {
filter := make([]string, 0)
for _, v := range *f.SearchStringPlain {
filter = append(filter, fmt.Sprintf("instr(lower(messages.channel_internal_name), lower(:%s))", params.Add(v)))
filter = append(filter, fmt.Sprintf("instr(lower(messages.sender_name), lower(:%s))", params.Add(v)))
filter = append(filter, fmt.Sprintf("instr(lower(messages.title), lower(:%s))", params.Add(v)))
filter = append(filter, fmt.Sprintf("instr(lower(messages.content), lower(:%s))", params.Add(v)))
}
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")")
}
sqlClause := ""
if len(sqlClauses) > 0 {
sqlClause = strings.Join(sqlClauses, " AND ")
} else {
sqlClause = "1=1"
}
return sqlClause, joinClause, params, nil
}
func (f MessageFilter) Hash() string {
bh, err := dataext.StructHash(f, dataext.StructHashOptions{HashAlgo: sha512.New()})
if err != nil {
return "00000000"
}
str := hex.EncodeToString(bh)
return str[0:mathext.Min(8, len(bh))]
}