Skip to content

Commit

Permalink
added some stats on cleanup and startup
Browse files Browse the repository at this point in the history
  • Loading branch information
Szer committed Oct 4, 2023
1 parent d3d9ee6 commit 5cf40a6
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 7 deletions.
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# envs
BOT_TELEGRAM_TOKEN=SECRET_FROM_TELEGRAM
BOT_AUTH_TOKEN=JUST_YOUR_SECRET
BOT_HOOK_ROUTE=/bot
ASPNETCORE_URLS=http://+:88
DEBUG=true
LOGS_CHANNEL_ID=-1000000000000
ALLOWED_USERS={"you":"123467890"}
CHATS_TO_MONITOR={"your_channel":"-100123467890"}
DATABASE_URL=AMAZING_ADO_NET_CONNECTION_STRING
OTEL_EXPORTER_ZIPKIN_ENDPOINT=http://zipkin:9411/api/v2/spans
OTEL_EXPORTER_CONSOLE=false
IGNORE_SIDE_EFFECTS=true
2 changes: 1 addition & 1 deletion src/VahterBanBot/Bot.fs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ let aggregateBanResultInLogMsg
let targetUserId = message.ReplyToMessage.From.Id
let targetUsername = message.ReplyToMessage.From.Username
let logMsgBuilder = StringBuilder()
%logMsgBuilder.Append($"Vahter {prependUsername vahterUsername}({vahterUserId}) banned {prependUsername targetUsername} ({targetUserId}) in chat ${chatName}({chatId})")
%logMsgBuilder.Append($"Vahter {prependUsername vahterUsername}({vahterUserId}) banned {prependUsername targetUsername} ({targetUserId}) in {prependUsername chatName}({chatId})")

// we don't want to spam logs channel if all is good
let allChatsOk = banResults |> Array.forall Result.isOk
Expand Down
10 changes: 8 additions & 2 deletions src/VahterBanBot/Cleanup.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module VahterBanBot.Cleanup

open System.Text
open System.Threading.Tasks
open Microsoft.Extensions.Logging
open Telegram.Bot
Expand Down Expand Up @@ -27,9 +28,13 @@ type CleanupService(

let cleanup _ = task {
let! cleanupMsgs = DB.cleanupOldMessages cleanupOldLimit
let! vahterStats = DB.getVahterStats (Some cleanupInterval)

let msg = $"Cleaned up {cleanupMsgs} messages"
let sb = StringBuilder()
%sb.AppendLine $"Cleaned up {cleanupMsgs} messages from DB which are older than {timeSpanAsHumanReadable cleanupOldLimit}"
%sb.AppendLine(string vahterStats)

let msg = sb.ToString()
let! _ = telegramClient.SendTextMessageAsync(
ChatId(botConf.LogsChannelId),
msg
Expand All @@ -39,7 +44,8 @@ type CleanupService(

interface IHostedService with
member this.StartAsync(cancellationToken) =
timer <- new Timer(TimerCallback(cleanup >> ignore), null, TimeSpan.Zero, cleanupInterval)
if not botConf.IgnoreSideEffects then
timer <- new Timer(TimerCallback(cleanup >> ignore), null, TimeSpan.Zero, cleanupInterval)
Task.CompletedTask

member this.StopAsync(cancellationToken) =
Expand Down
21 changes: 21 additions & 0 deletions src/VahterBanBot/DB.fs
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,24 @@ let cleanupOldMessages (howOld: TimeSpan): Task<int> =

return messagesDeleted
}

let getVahterStats(banInterval: TimeSpan option): Task<VahterStats> =
task {
use conn = new NpgsqlConnection(connString)

//language=postgresql
let sql =
"""
SELECT vahter.username AS vahter
, COUNT(*) AS killCountTotal
, COUNT(*) FILTER (WHERE u.banned_at > NOW() - @banInterval::INTERVAL) AS killCountInterval
FROM "user" u
JOIN "user" vahter ON vahter.id = u.banned_by
WHERE u.banned_by IS NOT NULL
GROUP BY u.banned_by, vahter.username
ORDER BY killCountTotal DESC
"""

let! stats = conn.QueryAsync<VahterStat>(sql, {| banInterval = banInterval |})
return { interval = banInterval; stats = Array.ofSeq stats }
}
14 changes: 11 additions & 3 deletions src/VahterBanBot/Program.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
open System.Collections.Generic
open System
open System.Collections.Generic
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
Expand All @@ -8,6 +9,7 @@ open Telegram.Bot
open Telegram.Bot.Types
open Giraffe
open Microsoft.Extensions.DependencyInjection
open VahterBanBot
open VahterBanBot.Cleanup
open VahterBanBot.Utils
open VahterBanBot.Bot
Expand All @@ -29,7 +31,8 @@ let botConf =
LogsChannelId = getEnv "LOGS_CHANNEL_ID" |> int64
ChatsToMonitor = getEnv "CHATS_TO_MONITOR" |> JsonConvert.DeserializeObject<_>
AllowedUsers = getEnv "ALLOWED_USERS" |> JsonConvert.DeserializeObject<_>
ShouldDeleteChannelMessages = getEnvOr "SHOULD_DELETE_CHANNEL_MESSAGES" "true" |> bool.Parse }
ShouldDeleteChannelMessages = getEnvOr "SHOULD_DELETE_CHANNEL_MESSAGES" "true" |> bool.Parse
IgnoreSideEffects = getEnvOr "IGNORE_SIDE_EFFECTS" "false" |> bool.Parse }

let validateApiKey (ctx : HttpContext) =
match ctx.TryGetRequestHeader "X-Telegram-Bot-Api-Secret-Token" with
Expand Down Expand Up @@ -128,9 +131,14 @@ let startLogMsg =
%sb.AppendLine("ChatsToMonitor:")
for KeyValue(username, chatId) in botConf.ChatsToMonitor do
%sb.AppendLine($" {prependUsername username} ({chatId})")

let totalStats = (DB.getVahterStats None).Result
%sb.AppendLine (string totalStats)

sb.ToString()

app.Logger.LogInformation startLogMsg
telegramClient.SendTextMessageAsync(ChatId(botConf.LogsChannelId), startLogMsg).Wait()
if not botConf.IgnoreSideEffects then
telegramClient.SendTextMessageAsync(ChatId(botConf.LogsChannelId), startLogMsg).Wait()

server.Wait()
39 changes: 38 additions & 1 deletion src/VahterBanBot/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

open System
open System.Collections.Generic
open System.Text
open Utils

[<CLIMutable>]
type BotConfiguration =
Expand All @@ -11,7 +13,8 @@ type BotConfiguration =
LogsChannelId: int64
ChatsToMonitor: Dictionary<string, int64>
AllowedUsers: Dictionary<string, int64>
ShouldDeleteChannelMessages: bool }
ShouldDeleteChannelMessages: bool
IgnoreSideEffects: bool }

[<CLIMutable>]
type DbUser =
Expand Down Expand Up @@ -56,3 +59,37 @@ type DbMessage =
Message_Id = message.MessageId
User_Id = message.From.Id
Created_At = DateTime.UtcNow }

[<CLIMutable>]
type VahterStat =
{ Vahter: string
KillCountTotal: int
KillCountInterval: int }

type VahterStats =
{ stats: VahterStat array
interval: TimeSpan option }
override this.ToString() =
let sb = StringBuilder()
if this.stats.Length > 0 then
if this.interval.IsSome then
let intervalKills =
this.stats
|> Array.filter (fun x -> x.KillCountInterval > 0)

if intervalKills.Length > 0 then
%sb.AppendLine $"Vahter stats for the last {timeSpanAsHumanReadable this.interval.Value}"

intervalKills
|> Array.sortByDescending (fun x -> x.KillCountInterval)
|> Array.iteri (fun i stat ->
%sb.AppendLine $"%d{i+1} {prependUsername stat.Vahter} - {stat.KillCountInterval}")
else
%sb.AppendLine $"No one was killed in the last {timeSpanAsHumanReadable this.interval.Value}"

%sb.AppendLine "Vahter stats all time"
this.stats
|> Array.sortByDescending (fun x -> x.KillCountTotal)
|> Array.iteri (fun i stat ->
%sb.AppendLine $"%d{i+1} {stat.Vahter} - {stat.KillCountTotal}")
sb.ToString()
11 changes: 11 additions & 0 deletions src/VahterBanBot/Utils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,14 @@ let prependUsername (s: string) =
elif s.StartsWith "@" then
s
else "@" + s

let timeSpanAsHumanReadable (ts: TimeSpan) =
let totalSeconds = ts.TotalSeconds
if totalSeconds < 60.0 then
$"%.1f{totalSeconds} seconds"
elif totalSeconds < 3600.0 then
$"%.1f{ts.TotalMinutes} minutes"
elif totalSeconds < 86400.0 then
$"%.1f{ts.TotalHours} hours"
else
$"%.1f{ts.TotalDays} days"

0 comments on commit 5cf40a6

Please sign in to comment.