Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

even more message data #24

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/VahterBanBot.Tests/ContainerTestBase.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ open Npgsql
open Telegram.Bot.Types
open Testcontainers.PostgreSql
open VahterBanBot.Tests.TgMessageUtils
open VahterBanBot.Types
open Xunit
open Dapper

Expand Down Expand Up @@ -158,12 +159,12 @@ type VahterTestContainers() =
Tg.chat(id = -42, username = "dotnetru")
]

member _.MessageExist(msg: Message) = task {
member _.TryGetDbMessage(msg: Message) = task {
use conn = new NpgsqlConnection(publicConnectionString)
//language=postgresql
let sql = "SELECT COUNT(*) FROM message WHERE chat_id = @chatId AND message_id = @messageId"
let! count = conn.QuerySingleAsync<int>(sql, {| chatId = msg.Chat.Id; messageId = msg.MessageId |})
return count = 1
let sql = "SELECT * FROM message WHERE chat_id = @chatId AND message_id = @messageId"
let! dbMessage = conn.QueryAsync<DbMessage>(sql, {| chatId = msg.Chat.Id; messageId = msg.MessageId |})
return dbMessage |> Seq.tryHead
}

member _.MessageBanned(msg: Message) = task {
Expand Down
38 changes: 38 additions & 0 deletions src/VahterBanBot.Tests/MessageTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module VahterBanBot.Tests.MessageTests

open System
open System.Net
open VahterBanBot.Types
open Telegram.Bot.Types
open Telegram.Bot.Types.Enums
open VahterBanBot.Tests.ContainerTestBase
open VahterBanBot.Tests.TgMessageUtils
open Xunit

type MessageTests(fixture: VahterTestContainers) =

[<Fact>]
let ``All data from the message being saved`` () = task {
// record just a message with some additional data
let msgUpdate = Tg.quickMsg(chat = fixture.ChatsToMonitor[0])
msgUpdate.Message.Entities <- [| MessageEntity(Type = MessageEntityType.Code, Offset = 0, Length = 6) |]
msgUpdate.Message.Sticker <- Sticker(Type = StickerType.Mask, Width = 512, Height = 512, FileId = "sticker-id", FileUniqueId = "sticker-uid")
let! _ = fixture.SendMessage msgUpdate

// assert that the message got recorded correctly
let! dbMsg = fixture.TryGetDbMessage msgUpdate.Message
Assert.True dbMsg.IsSome

let msg = msgUpdate.Message
let date = DateTimeOffset(msg.Date).ToUnixTimeSeconds()

Assert.Equal(
dbMsg.Value,
{ chat_id = msgUpdate.Message.Chat.Id
message_id = msgUpdate.Message.MessageId
user_id = msgUpdate.Message.From.Id
text = msgUpdate.Message.Text
raw_message = $"""{{"chat": {{"id": -666, "type": "unknown", "username": "pro.hell"}}, "date": {date}, "from": {{"id": {msg.From.Id}, "is_bot": false, "first_name": "{msg.From.FirstName}"}}, "text": "{msg.Text}", "sticker": {{"type": "mask", "width": 512, "height": 512, "file_id": "sticker-id", "is_video": false, "is_animated": false, "file_unique_id": "sticker-uid"}}, "entities": [{{"type": "code", "length": 6, "offset": 0}}], "message_id": {msg.MessageId}}}"""
created_at = dbMsg.Value.created_at }
)
}
16 changes: 8 additions & 8 deletions src/VahterBanBot.Tests/PingTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ type PingTests(fixture: VahterTestContainers) =
let msg = Tg.quickMsg(chat = fixture.ChatsToMonitor[0])

// assert that the message is not in the db
let! msgExist = fixture.MessageExist msg.Message
Assert.False msgExist
let! dbMsg = fixture.TryGetDbMessage msg.Message
Assert.False dbMsg.IsSome

// send the message to the bot
let! resp = fixture.SendMessage msg
Assert.Equal(HttpStatusCode.OK, resp.StatusCode)

// assert that the message is in the db
let! msgExist = fixture.MessageExist msg.Message
Assert.True msgExist
let! dbMsg = fixture.TryGetDbMessage msg.Message
Assert.True dbMsg.IsSome
}

[<Fact>]
Expand All @@ -31,16 +31,16 @@ type PingTests(fixture: VahterTestContainers) =
let msg = Tg.quickMsg(chat = Tg.chat())

// assert that the message is not in the db
let! msgExist = fixture.MessageExist msg.Message
Assert.False msgExist
let! dbMsg = fixture.TryGetDbMessage msg.Message
Assert.False dbMsg.IsSome

// send the message to the bot
let! resp = fixture.SendMessage msg
Assert.Equal(HttpStatusCode.OK, resp.StatusCode)

// assert that the message is still not in the db
let! msgExist = fixture.MessageExist msg.Message
Assert.False msgExist
let! dbMsg = fixture.TryGetDbMessage msg.Message
Assert.False dbMsg.IsSome
}

interface IAssemblyFixture<VahterTestContainers>
1 change: 1 addition & 0 deletions src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<Compile Include="TgMessageUtils.fs" />
<Compile Include="ContainerTestBase.fs" />
<Compile Include="BaseTests.fs" />
<Compile Include="MessageTests.fs" />
<Compile Include="BanTests.fs" />
<Compile Include="PingTests.fs" />
<Compile Include="Program.fs"/>
Expand Down
14 changes: 7 additions & 7 deletions src/VahterBanBot/Bot.fs
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,11 @@ let banOnReply
use _ =
botActivity
.StartActivity("deleteMsg")
.SetTag("msgId", msg.Message_Id)
.SetTag("chatId", msg.Chat_Id)
do! botClient.DeleteMessageAsync(ChatId(msg.Chat_Id), msg.Message_Id)
.SetTag("msgId", msg.message_id)
.SetTag("chatId", msg.chat_id)
do! botClient.DeleteMessageAsync(ChatId(msg.chat_id), msg.message_id)
with e ->
logger.LogError ($"Failed to delete message {msg.Message_Id} from chat {msg.Chat_Id}", e)
logger.LogError ($"Failed to delete message {msg.message_id} from chat {msg.chat_id}", e)
})
|> Task.WhenAll
|> taskIgnore
Expand Down Expand Up @@ -363,9 +363,9 @@ let unban

let! user = DB.getUserById targetUserId
if user.IsSome then
%banOnReplyActivity.SetTag("targetUsername", user.Value.Username)
%banOnReplyActivity.SetTag("targetUsername", user.Value.username)

let targetUsername = user |> Option.bind (fun u -> u.Username)
let targetUsername = user |> Option.bind (_.username)

// try unban user in all monitored chats
let! unbanResults = unbanInAllChats botConfig botClient targetUserId
Expand Down Expand Up @@ -505,7 +505,7 @@ let onUpdate
if isNull message || isNull message.From then
logger.LogWarning "Received update without message"
else

// early return if we don't monitor this chat
if not (botConfig.ChatsToMonitor.ContainsValue message.Chat.Id) then
()
Expand Down
39 changes: 8 additions & 31 deletions src/VahterBanBot/DB.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,14 @@ let upsertUser (user: DbUser): Task<DbUser> =
let sql =
"""
INSERT INTO "user" (id, username, created_at, updated_at)
VALUES (@id, @username, @createdAt, @updatedAt)
VALUES (@id, @username, @created_at, @updated_at)
ON CONFLICT (id) DO UPDATE
SET username = COALESCE("user".username, EXCLUDED.username),
updated_at = GREATEST(EXCLUDED.updated_at, "user".updated_at)
RETURNING *;
"""

let! insertedUser =
conn.QueryAsync<DbUser>(
sql,
{| id = user.Id
username = user.Username
createdAt = user.Created_At
updatedAt = user.Updated_At |}
)
let! insertedUser = conn.QueryAsync<DbUser>(sql, user)

return insertedUser |> Seq.head
}
Expand All @@ -43,19 +36,12 @@ let insertMessage (message: DbMessage): Task<DbMessage> =
//language=postgresql
let sql =
"""
INSERT INTO message (chat_id, message_id, user_id, created_at)
VALUES (@chatId, @messageId, @userId, @createdAt)
INSERT INTO message (chat_id, message_id, user_id, text, raw_message, created_at)
VALUES (@chat_id, @message_id, @user_id, @text, @raw_message::JSONB, @created_at)
ON CONFLICT (chat_id, message_id) DO NOTHING RETURNING *;
"""

let! insertedMessage =
conn.QueryAsync<DbMessage>(
sql,
{| chatId = message.Chat_Id
messageId = message.Message_Id
userId = message.User_Id
createdAt = message.Created_At |}
)
let! insertedMessage = conn.QueryAsync<DbMessage>(sql, message)

return
insertedMessage
Expand All @@ -71,19 +57,10 @@ let banUser (banned: DbBanned): Task =
let sql =
"""
INSERT INTO banned (message_id, message_text, banned_user_id, banned_at, banned_in_chat_id, banned_in_chat_username, banned_by)
VALUES (@messageId, @messageText, @bannedUserId, @bannedAt, @bannedInChatId, @bannedInChatUsername, @bannedBy)
VALUES (@message_id, @message_text, @banned_user_id, @banned_at, @banned_in_chat_id, @banned_in_chat_username, @banned_by)
"""

let! _ = conn.ExecuteAsync(
sql,
{| messageId = banned.Message_Id
messageText = banned.Message_text
bannedUserId = banned.Banned_User_Id
bannedAt = banned.Banned_At
bannedInChatId = banned.Banned_In_Chat_Id
bannedInChatUsername = banned.Banned_In_Chat_username
bannedBy = banned.Banned_By |}
)
let! _ = conn.ExecuteAsync(sql, banned)
return banned
}

Expand All @@ -100,7 +77,7 @@ let getUserMessages (userId: int64): Task<DbMessage array> =

let deleteMsgs (msg: DbMessage[]): Task<int> =
task {
let msgIds = msg |> Array.map (fun m -> m.Message_Id)
let msgIds = msg |> Array.map (_.message_id)
use conn = new NpgsqlConnection(connString)

//language=postgresql
Expand Down
65 changes: 35 additions & 30 deletions src/VahterBanBot/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
open System
open System.Collections.Generic
open System.Text
open Newtonsoft.Json
open Utils

[<CLIMutable>]
Expand All @@ -23,52 +24,56 @@ type BotConfiguration =

[<CLIMutable>]
type DbUser =
{ Id: int64
Username: string option
Updated_At: DateTime
Created_At: DateTime }
{ id: int64
username: string option
updated_at: DateTime
created_at: DateTime }

static member newUser(id, ?username: string) =
{ Id = id
Username = username
Updated_At = DateTime.UtcNow
Created_At = DateTime.UtcNow }
{ id = id
username = username
updated_at = DateTime.UtcNow
created_at = DateTime.UtcNow }

static member newUser(user: Telegram.Bot.Types.User) =
DbUser.newUser (id = user.Id, ?username = Option.ofObj user.Username)

[<CLIMutable>]
type DbBanned =
{ Message_Id: int option
Message_text: string
Banned_User_Id: int64
Banned_At: DateTime
Banned_In_Chat_Id: int64 option
Banned_In_Chat_username: string option
Banned_By: int64 }
{ message_id: int option
message_text: string
banned_user_id: int64
banned_at: DateTime
banned_in_chat_id: int64 option
banned_in_chat_username: string option
banned_by: int64 }
module DbBanned =
let banMessage (vahter: int64) (message: Telegram.Bot.Types.Message) =
if isNull message.From || isNull message.Chat then
failwith "Message should have a user and a chat"
{ Message_Id = Some message.MessageId
Message_text = message.Text
Banned_User_Id = message.From.Id
Banned_At = DateTime.UtcNow
Banned_In_Chat_Id = Some message.Chat.Id
Banned_In_Chat_username = Some message.Chat.Username
Banned_By = vahter }
{ message_id = Some message.MessageId
message_text = message.Text
banned_user_id = message.From.Id
banned_at = DateTime.UtcNow
banned_in_chat_id = Some message.Chat.Id
banned_in_chat_username = Some message.Chat.Username
banned_by = vahter }

[<CLIMutable>]
type DbMessage =
{ Chat_Id: int64
Message_Id: int
User_Id: int64
Created_At: DateTime }
{ chat_id: int64
message_id: int
user_id: int64
text: string
raw_message: string
created_at: DateTime }
static member newMessage(message: Telegram.Bot.Types.Message) =
{ Chat_Id = message.Chat.Id
Message_Id = message.MessageId
User_Id = message.From.Id
Created_At = DateTime.UtcNow }
{ chat_id = message.Chat.Id
message_id = message.MessageId
user_id = message.From.Id
created_at = DateTime.UtcNow
text = message.Text
raw_message = JsonConvert.SerializeObject message }

[<CLIMutable>]
type VahterStat =
Expand Down
5 changes: 5 additions & 0 deletions src/migrations/V4__even-more-messages-info.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE message
ADD COLUMN text TEXT,
ADD COLUMN raw_message JSONB;

CREATE INDEX message_raw_message_idx ON message USING GIN (raw_message);