Add some comments

Signed-off-by: baalajimaestro <me@baalajimaestro.me>
This commit is contained in:
baalajimaestro 2022-04-11 11:45:19 +05:30
parent a8bde2f198
commit 278a256d64
Signed by: baalajimaestro
GPG key ID: F93C394FE9BBAFD5

View file

@ -9,9 +9,11 @@ import telebot
import std/[asyncdispatch, logging, options, strutils, random, with, os, tables, times, sequtils] import std/[asyncdispatch, logging, options, strutils, random, with, os, tables, times, sequtils]
import norm/[model, sqlite] import norm/[model, sqlite]
# Logging Level
var L = newConsoleLogger(fmtStr="$levelname, [$time] ") var L = newConsoleLogger(fmtStr="$levelname, [$time] ")
addHandler(L) addHandler(L)
# Custom Types Defined to handle tables on norm
type type
CensoredData* = ref object of Model CensoredData* = ref object of Model
ftype*: string ftype*: string
@ -26,23 +28,29 @@ type
bantime*: float bantime*: float
bantype*: string bantype*: string
# Hashmaps for RateLimiting and working around telegram's album issue
var RateLimiter = initTable[int64, seq[float]]() var RateLimiter = initTable[int64, seq[float]]()
var GroupMedia = initTable[int, string]() var GroupMedia = initTable[int, string]()
# Bot Admins, is comma-separated variable
let AdminID = getEnv("ADMIN_ID").split(",") let AdminID = getEnv("ADMIN_ID").split(",")
let dbConn* = getDb() let dbConn* = getDb()
# Functions to assist adding/deleting entries from tables
func NewCensoredData*(ftype = ""; fhash = ""; fileid = ""; time = 0.0; caption = ""): func NewCensoredData*(ftype = ""; fhash = ""; fileid = ""; time = 0.0; caption = ""):
CensoredData = CensoredData(ftype: ftype, fhash: fhash, fileid: fileid, time: time, caption: caption) CensoredData = CensoredData(ftype: ftype, fhash: fhash, fileid: fileid, time: time, caption: caption)
func NewBannedUsers*(userid = int64(0), bantime = 0.0, bantype = ""): func NewBannedUsers*(userid = int64(0), bantime = 0.0, bantype = ""):
BannedUsers = BannedUsers(userid: userid, bantime: bantime, bantype: bantype) BannedUsers = BannedUsers(userid: userid, bantime: bantime, bantype: bantype)
# Create tables if they dont exist
dbConn.createTables(NewCensoredData()) dbConn.createTables(NewCensoredData())
dbConn.createTables(NewBannedUsers()) dbConn.createTables(NewBannedUsers())
# Ratelimits a user if there is more than 20 timestamps within 60 secs.
# Resets their ratelimit counter if 60s has passed since first timestamp
proc ManageRateLimit(): void= proc ManageRateLimit(): void=
for i in RateLimiter.keys().toSeq(): for i in RateLimiter.keys().toSeq():
if RateLimiter[i][^1] - RateLimiter[i][0] >= 60: if RateLimiter[i][^1] - RateLimiter[i][0] >= 60:
@ -59,6 +67,8 @@ proc ManageRateLimit(): void=
var e = i var e = i
dbConn.delete(e) dbConn.delete(e)
# Cleanup old data to save space
# Deletes old data after 6 months of entry
proc OldDataCleanup(): void= proc OldDataCleanup(): void=
if dbConn.exists(CensoredData, "? - time >= 15780000", epochTime()): if dbConn.exists(CensoredData, "? - time >= 15780000", epochTime()):
var TempData = @[NewCensoredData()] var TempData = @[NewCensoredData()]
@ -67,6 +77,7 @@ proc OldDataCleanup(): void=
var e = i var e = i
dbConn.delete(e) dbConn.delete(e)
# Generates a 6-character magic that will be used to identify the file
proc generate_hash(): string= proc generate_hash(): string=
result = newString(7) result = newString(7)
const charset = {'a' .. 'z', 'A' .. 'Z', '0' .. '9'} const charset = {'a' .. 'z', 'A' .. 'Z', '0' .. '9'}
@ -74,18 +85,24 @@ proc generate_hash(): string=
result[i] = sample(charset) result[i] = sample(charset)
return result return result
# Main update handler
proc updateHandler(b: Telebot, u: Update): Future[bool] {.async, gcsafe.} = proc updateHandler(b: Telebot, u: Update): Future[bool] {.async, gcsafe.} =
let response = u.message.get let response = u.message.get
# Refresh rate-limits
ManageRateLimit() ManageRateLimit()
# Dont bother about rate-limited/banned users
if not dbConn.exists(BannedUsers, "userid = ?", response.chat.id): if not dbConn.exists(BannedUsers, "userid = ?", response.chat.id):
# Update rate-limit counter
if RateLimiter.contains(response.chat.id): if RateLimiter.contains(response.chat.id):
RateLimiter[response.chat.id].insert(epochTime()) RateLimiter[response.chat.id].insert(epochTime())
else: else:
RateLimiter[response.chat.id] = @[epochTime()] RateLimiter[response.chat.id] = @[epochTime()]
if response.text.isSome: if response.text.isSome:
let message = response.text.get let message = response.text.get
# Respond to /start without payload
if message == "/start": if message == "/start":
discard await b.sendMessage(response.chat.id, "Hey, To create a censored post, you can share any album, video, photo, gif, sticker, etc. The messages could then be forwarded to any chat for them to view") discard await b.sendMessage(response.chat.id, "Hey, To create a censored post, you can share any album, video, photo, gif, sticker, etc. The messages could then be forwarded to any chat for them to view")
# Perma-ban, available to only admins
elif message.contains("/ban"): elif message.contains("/ban"):
if $response.chat.id in AdminID: if $response.chat.id in AdminID:
let user = message.split(" ") let user = message.split(" ")
@ -93,6 +110,7 @@ proc updateHandler(b: Telebot, u: Update): Future[bool] {.async, gcsafe.} =
with dbConn: with dbConn:
insert BannedUser insert BannedUser
discard await b.sendMessage(response.chat.id, "Banned!") discard await b.sendMessage(response.chat.id, "Banned!")
# Start, but with payload, handles sending the censored media
elif message.contains("/start"): elif message.contains("/start"):
let deeplink = message.split(" ") let deeplink = message.split(" ")
if not dbConn.exists(CensoredData, "fhash = ?", deeplink[1]): if not dbConn.exists(CensoredData, "fhash = ?", deeplink[1]):
@ -121,6 +139,7 @@ proc updateHandler(b: Telebot, u: Update): Future[bool] {.async, gcsafe.} =
discard await b.sendAnimation(response.chat.id, TempData[0].fileid, caption=TempData[0].caption) discard await b.sendAnimation(response.chat.id, TempData[0].fileid, caption=TempData[0].caption)
elif TempData[0].ftype == "sticker": elif TempData[0].ftype == "sticker":
discard await b.sendSticker(response.chat.id, TempData[0].fileid) discard await b.sendSticker(response.chat.id, TempData[0].fileid)
# Its not a text, its some media, lets censor it!
else: else:
var fileid = "" var fileid = ""
var ftype = "" var ftype = ""
@ -145,7 +164,8 @@ proc updateHandler(b: Telebot, u: Update): Future[bool] {.async, gcsafe.} =
elif response.sticker.isSome: elif response.sticker.isSome:
fileid = response.sticker.get.fileId fileid = response.sticker.get.fileId
ftype = "sticker" ftype = "sticker"
# Workaround for groupmedia using hashtables
# Telegram is sending multiple updates, instead of 1, so just workaround it
if response.mediaGroupId.isSome: if response.mediaGroupId.isSome:
if parseInt(response.mediaGroupId.get) notin GroupMedia.keys().toSeq(): if parseInt(response.mediaGroupId.get) notin GroupMedia.keys().toSeq():
let filehash = generate_hash() let filehash = generate_hash()
@ -168,6 +188,7 @@ proc updateHandler(b: Telebot, u: Update): Future[bool] {.async, gcsafe.} =
discard await b.sendMessage(response.chat.id, "*Censored " & capitalizeAscii(ftype) & "*\n\n[Tap to View](tg://resolve?domain=" & b.username & "&start=" & filehash & ")", parseMode = "Markdown") discard await b.sendMessage(response.chat.id, "*Censored " & capitalizeAscii(ftype) & "*\n\n[Tap to View](tg://resolve?domain=" & b.username & "&start=" & filehash & ")", parseMode = "Markdown")
OldDataCleanup() OldDataCleanup()
# Start the bot
let bot = newTeleBot(getEnv("TELEGRAM_TOKEN")) let bot = newTeleBot(getEnv("TELEGRAM_TOKEN"))
bot.onUpdate(updateHandler) bot.onUpdate(updateHandler)
bot.poll(timeout=300) bot.poll(timeout=300)