2021-08-27 15:03:47 +00:00
using System.Collections.Generic ;
2020-08-29 11:46:27 +00:00
using System.Linq ;
using System.Threading.Tasks ;
using Dapper ;
2021-09-30 01:51:38 +00:00
using SqlKata ;
2020-08-29 11:46:27 +00:00
namespace PluralKit.Core
{
public partial class ModelRepository
{
2021-09-30 01:51:38 +00:00
public Task AddMessage ( PKMessage msg )
2021-08-27 15:03:47 +00:00
{
2021-09-30 01:51:38 +00:00
var query = new Query ( "messages" ) . AsInsert ( new
{
mid = msg . Mid ,
guild = msg . Guild ,
channel = msg . Channel ,
member = msg . Member ,
sender = msg . Sender ,
original_mid = msg . OriginalMid ,
} ) ;
2020-08-29 11:46:27 +00:00
_logger . Debug ( "Stored message {@StoredMessage} in channel {Channel}" , msg , msg . Channel ) ;
2021-09-30 01:51:38 +00:00
// "on conflict do nothing" in the (pretty rare) case of duplicate events coming in from Discord, which would lead to a DB error before
return _db . ExecuteQuery ( query , extraSql : "on conflict do nothing" ) ;
2020-08-29 11:46:27 +00:00
}
2021-08-27 15:03:47 +00:00
2021-09-30 01:51:38 +00:00
// todo: add a Mapper to QuerySingle and move this to SqlKata
2021-08-08 19:56:24 +00:00
public async Task < FullMessage ? > GetMessage ( IPKConnection conn , ulong id )
2020-08-29 11:46:27 +00:00
{
FullMessage Mapper ( PKMessage msg , PKMember member , PKSystem system ) = >
2021-08-27 15:03:47 +00:00
new FullMessage { Message = msg , System = system , Member = member } ;
2020-08-29 11:46:27 +00:00
var result = await conn . QueryAsync < PKMessage , PKMember , PKSystem , FullMessage > (
"select messages.*, members.*, systems.* from messages, members, systems where (mid = @Id or original_mid = @Id) and messages.member = members.id and systems.id = members.system" ,
2021-08-27 15:03:47 +00:00
Mapper , new { Id = id } ) ;
2020-08-29 11:46:27 +00:00
return result . FirstOrDefault ( ) ;
}
2021-09-30 01:51:38 +00:00
public async Task DeleteMessage ( ulong id )
2020-08-29 11:46:27 +00:00
{
2021-09-30 01:51:38 +00:00
var query = new Query ( "messages" ) . AsDelete ( ) . Where ( "mid" , id ) ;
var rowCount = await _db . ExecuteQuery ( query ) ;
2020-08-29 11:46:27 +00:00
if ( rowCount > 0 )
_logger . Information ( "Deleted message {MessageId} from database" , id ) ;
}
2021-09-30 01:51:38 +00:00
public async Task DeleteMessagesBulk ( IReadOnlyCollection < ulong > ids )
2020-08-29 11:46:27 +00:00
{
// Npgsql doesn't support ulongs in general - we hacked around it for plain ulongs but tbh not worth it for collections of ulong
// Hence we map them to single longs, which *are* supported (this is ok since they're Technically (tm) stored as signed longs in the db anyway)
2021-09-30 01:51:38 +00:00
var query = new Query ( "messages" ) . AsDelete ( ) . WhereIn ( "mid" , ids . Select ( id = > ( long ) id ) . ToArray ( ) ) ;
var rowCount = await _db . ExecuteQuery ( query ) ;
2020-08-29 11:46:27 +00:00
if ( rowCount > 0 )
_logger . Information ( "Bulk deleted messages ({FoundCount} found) from database: {MessageIds}" , rowCount ,
ids ) ;
}
2021-05-03 10:33:30 +00:00
2021-09-30 01:51:38 +00:00
public Task < PKMessage ? > GetLastMessage ( ulong guildId , ulong channelId , ulong accountId )
2021-05-03 10:33:30 +00:00
{
// Want to index scan on the (guild, sender, mid) index so need the additional constraint
2021-09-30 01:51:38 +00:00
var query = new Query ( "messages" )
. Where ( "guild" , guildId )
. Where ( "channel" , channelId )
. Where ( "sender" , accountId )
. OrderByDesc ( "mid" )
. Limit ( 1 ) ;
return _db . QueryFirst < PKMessage ? > ( query ) ;
2021-05-03 10:33:30 +00:00
}
2020-08-29 11:46:27 +00:00
}
public class PKMessage
{
public ulong Mid { get ; set ; }
public ulong? Guild { get ; set ; } // null value means "no data" (ie. from before this field being added)
public ulong Channel { get ; set ; }
public MemberId Member { get ; set ; }
public ulong Sender { get ; set ; }
public ulong? OriginalMid { get ; set ; }
}
2021-08-27 15:03:47 +00:00
2020-08-29 11:46:27 +00:00
public class FullMessage
{
public PKMessage Message ;
public PKMember Member ;
public PKSystem System ;
}
}