Mohammad Shahid commited on
Commit
aa300d8
·
1 Parent(s): 2fa5701

added user managment

Browse files
cmd/fsb/main.go CHANGED
@@ -1,7 +1,7 @@
1
  package main
2
 
3
  import (
4
- "EverythingSuckz/fsb/config"
5
  "fmt"
6
  "os"
7
 
 
1
  package main
2
 
3
  import (
4
+ "TelegramCloud/tgf/config"
5
  "fmt"
6
  "os"
7
 
cmd/fsb/run.go CHANGED
@@ -3,14 +3,14 @@
3
  package main
4
 
5
  import (
6
- "EverythingSuckz/fsb/config"
7
- "EverythingSuckz/fsb/internal/bot"
8
- "EverythingSuckz/fsb/internal/cache"
9
- "EverythingSuckz/fsb/internal/commands"
10
- "EverythingSuckz/fsb/internal/db"
11
- "EverythingSuckz/fsb/internal/routes"
12
- "EverythingSuckz/fsb/internal/types"
13
- "EverythingSuckz/fsb/internal/utils"
14
  "fmt"
15
  "net/http"
16
  "strings"
 
3
  package main
4
 
5
  import (
6
+ "TelegramCloud/tgf/config"
7
+ "TelegramCloud/tgf/internal/bot"
8
+ "TelegramCloud/tgf/internal/cache"
9
+ "TelegramCloud/tgf/internal/commands"
10
+ "TelegramCloud/tgf/internal/db"
11
+ "TelegramCloud/tgf/internal/routes"
12
+ "TelegramCloud/tgf/internal/types"
13
+ "TelegramCloud/tgf/internal/utils"
14
  "fmt"
15
  "net/http"
16
  "strings"
cmd/fsb/session.go CHANGED
@@ -3,7 +3,7 @@ package main
3
  import (
4
  "fmt"
5
 
6
- "EverythingSuckz/fsb/pkg/qrlogin"
7
 
8
  "github.com/spf13/cobra"
9
  )
 
3
  import (
4
  "fmt"
5
 
6
+ "TelegramCloud/tgf/pkg/qrlogin"
7
 
8
  "github.com/spf13/cobra"
9
  )
go.mod CHANGED
@@ -1,4 +1,4 @@
1
- module EverythingSuckz/fsb
2
 
3
  go 1.21.3
4
 
@@ -13,6 +13,8 @@ require (
13
  go.mongodb.org/mongo-driver v1.17.4 // Or a similar version number
14
  )
15
 
 
 
16
  require (
17
  github.com/AnimeKaizoku/cacher v1.0.1 // indirect
18
  github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -20,7 +22,6 @@ require (
20
  github.com/chenzhuoyu/iasm v0.9.1 // indirect
21
  github.com/dustin/go-humanize v1.0.1 // indirect
22
  github.com/glebarez/go-sqlite v1.22.0 // indirect
23
- github.com/glebarez/sqlite v1.11.0 // indirect
24
  github.com/go-faster/errors v0.7.1 // indirect
25
  github.com/go-faster/jx v1.1.0 // indirect
26
  github.com/go-faster/xor v1.0.0 // indirect
 
1
+ module TelegramCloud/tgf
2
 
3
  go 1.21.3
4
 
 
13
  go.mongodb.org/mongo-driver v1.17.4 // Or a similar version number
14
  )
15
 
16
+ require github.com/glebarez/sqlite v1.11.0
17
+
18
  require (
19
  github.com/AnimeKaizoku/cacher v1.0.1 // indirect
20
  github.com/cenkalti/backoff/v4 v4.3.0 // indirect
 
22
  github.com/chenzhuoyu/iasm v0.9.1 // indirect
23
  github.com/dustin/go-humanize v1.0.1 // indirect
24
  github.com/glebarez/go-sqlite v1.22.0 // indirect
 
25
  github.com/go-faster/errors v0.7.1 // indirect
26
  github.com/go-faster/jx v1.1.0 // indirect
27
  github.com/go-faster/xor v1.0.0 // indirect
go.sum CHANGED
@@ -4,12 +4,8 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1
4
  github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
5
  github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
6
  github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
7
- github.com/celestix/gotgproto v1.0.0-beta16 h1:xV0h7L1V3DFWJe+wcY7KAtHFuN1RkP07vANHO5fjq9Q=
8
- github.com/celestix/gotgproto v1.0.0-beta16/go.mod h1:Ey7AMTGRCXpG2iWR/eSFWwRrLCrmZ+l7HZq52NLEo7c=
9
  github.com/celestix/gotgproto v1.0.0-beta18 h1:7884H/il+mzNreOQ4SqoMa4S5njt3UmGPKZTxPu38fU=
10
  github.com/celestix/gotgproto v1.0.0-beta18/go.mod h1:osZOlN5irPByA0+3IPsZOH+Ibs0tOMSKmIdgGYEBRgE=
11
- github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
12
- github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
13
  github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
14
  github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
15
  github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -38,8 +34,6 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
38
  github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
39
  github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
40
  github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
41
- github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
42
- github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
43
  github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
44
  github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
45
  github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
@@ -59,16 +53,13 @@ github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtP
59
  github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
60
  github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
61
  github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
62
- github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
63
- github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
64
  github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
65
  github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
66
  github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
67
  github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
68
  github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
69
- github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
70
- github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
71
  github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
 
72
  github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
73
  github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
74
  github.com/gotd/contrib v0.19.0 h1:O6GvMrRVeFslIHLUcpaHVzcl9/5PcgR2jQTIIeTyds0=
@@ -77,8 +68,6 @@ github.com/gotd/ige v0.2.2 h1:XQ9dJZwBfDnOGSTxKXBGP4gMud3Qku2ekScRjDWWfEk=
77
  github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0=
78
  github.com/gotd/neo v0.1.5 h1:oj0iQfMbGClP8xI59x7fE/uHoTJD7NZH9oV1WNuPukQ=
79
  github.com/gotd/neo v0.1.5/go.mod h1:9A2a4bn9zL6FADufBdt7tZt+WMhvZoc5gWXihOPoiBQ=
80
- github.com/gotd/td v0.97.0 h1:EplGV6M6xFISLktsRFJZKm1NPyPjxR0XK9vbys0i/Qk=
81
- github.com/gotd/td v0.97.0/go.mod h1:6SwTJiw/fkw81QU+WHqB2HZ+38s0UJJH1a2nqwezCfA=
82
  github.com/gotd/td v0.105.0 h1:FjU9pgmL5Qt10+cosPCz4agvQT/hMBz6QMi1fFH7ekY=
83
  github.com/gotd/td v0.105.0/go.mod h1:aVe5/LP/nNIyAqaW3CwB0Ckum+MkcfvazwMOLHV0bqQ=
84
  github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -93,13 +82,9 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
93
  github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
94
  github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
95
  github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
96
- github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
97
- github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
98
  github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
99
  github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
100
  github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
101
- github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
102
- github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
103
  github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
104
  github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
105
  github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@@ -149,9 +134,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
149
  github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
150
  github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
151
  github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
152
- github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
153
  github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
154
  github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 
155
  github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
156
  github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
157
  github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@@ -165,16 +150,10 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi
165
  github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
166
  github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
167
  github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
168
- go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
169
- go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
170
  go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
171
  go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
172
- go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY=
173
- go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA=
174
  go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
175
  go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
176
- go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8=
177
- go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI=
178
  go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
179
  go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
180
  go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@@ -183,8 +162,6 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
183
  go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
184
  go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
185
  go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
186
- go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
187
- go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
188
  go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
189
  go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
190
  golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -192,29 +169,21 @@ golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
192
  golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
193
  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
194
  golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
195
- golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
196
- golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
197
- golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
198
- golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
199
  golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
200
  golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
201
  golang.org/x/exp v0.0.0-20230116083435-1de6713980de h1:DBWn//IJw30uYCgERoxCg84hWtA97F4wMiKOIh00Uf0=
202
  golang.org/x/exp v0.0.0-20230116083435-1de6713980de/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
203
  golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 
 
204
  golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
205
  golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
206
  golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
207
- golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
208
- golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
209
  golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
210
  golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
211
  golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
212
  golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
213
  golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
214
- golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
215
- golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
216
- golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
217
- golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
218
  golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
219
  golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
220
  golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -224,10 +193,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
224
  golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
225
  golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
226
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
227
- golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
228
- golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
229
- golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
230
- golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
231
  golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
232
  golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
233
  golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -236,10 +201,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
236
  golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
237
  golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
238
  golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
239
- golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
240
- golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
241
- golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
242
- golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
243
  golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
244
  golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
245
  golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
@@ -247,6 +208,8 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
247
  golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
248
  golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
249
  golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 
 
250
  golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
251
  google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
252
  google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
@@ -258,26 +221,32 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
258
  gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
259
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
260
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
261
- gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
262
- gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
263
  gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
264
  gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
265
- modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
266
- modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
 
 
 
 
 
 
267
  modernc.org/libc v1.55.2 h1:UN5eoBYrKp1b+gPYx8nZj5H7uxeybvyoQJfvcg+Bqjc=
268
  modernc.org/libc v1.55.2/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
269
  modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
270
  modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
271
- modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
272
- modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
273
  modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
274
  modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
275
- modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
276
- modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
 
 
277
  modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk=
278
  modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
279
- nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
280
- nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
 
 
281
  nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
282
  nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
283
  nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
 
4
  github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
5
  github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
6
  github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
 
 
7
  github.com/celestix/gotgproto v1.0.0-beta18 h1:7884H/il+mzNreOQ4SqoMa4S5njt3UmGPKZTxPu38fU=
8
  github.com/celestix/gotgproto v1.0.0-beta18/go.mod h1:osZOlN5irPByA0+3IPsZOH+Ibs0tOMSKmIdgGYEBRgE=
 
 
9
  github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
10
  github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
11
  github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 
34
  github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
35
  github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
36
  github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
 
 
37
  github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
38
  github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
39
  github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
 
53
  github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
54
  github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
55
  github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 
 
56
  github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
57
  github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
58
  github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
59
  github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
60
  github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 
 
61
  github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
62
+ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
63
  github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
64
  github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
65
  github.com/gotd/contrib v0.19.0 h1:O6GvMrRVeFslIHLUcpaHVzcl9/5PcgR2jQTIIeTyds0=
 
68
  github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0=
69
  github.com/gotd/neo v0.1.5 h1:oj0iQfMbGClP8xI59x7fE/uHoTJD7NZH9oV1WNuPukQ=
70
  github.com/gotd/neo v0.1.5/go.mod h1:9A2a4bn9zL6FADufBdt7tZt+WMhvZoc5gWXihOPoiBQ=
 
 
71
  github.com/gotd/td v0.105.0 h1:FjU9pgmL5Qt10+cosPCz4agvQT/hMBz6QMi1fFH7ekY=
72
  github.com/gotd/td v0.105.0/go.mod h1:aVe5/LP/nNIyAqaW3CwB0Ckum+MkcfvazwMOLHV0bqQ=
73
  github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 
82
  github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
83
  github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
84
  github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
 
 
85
  github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
86
  github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
87
  github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 
 
88
  github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
89
  github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
90
  github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
 
134
  github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
135
  github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
136
  github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 
137
  github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
138
  github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
139
+ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
140
  github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
141
  github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
142
  github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
 
150
  github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
151
  github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
152
  github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 
 
153
  go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
154
  go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
 
 
155
  go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
156
  go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
 
 
157
  go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
158
  go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
159
  go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
 
162
  go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
163
  go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
164
  go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 
 
165
  go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
166
  go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
167
  golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 
169
  golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
170
  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
171
  golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 
 
 
 
172
  golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
173
  golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
174
  golang.org/x/exp v0.0.0-20230116083435-1de6713980de h1:DBWn//IJw30uYCgERoxCg84hWtA97F4wMiKOIh00Uf0=
175
  golang.org/x/exp v0.0.0-20230116083435-1de6713980de/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
176
  golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
177
+ golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
178
+ golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
179
  golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
180
  golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
181
  golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 
 
182
  golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
183
  golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
184
  golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
185
  golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
186
  golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 
 
 
 
187
  golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
188
  golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
189
  golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
193
  golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
194
  golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
195
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 
 
 
 
196
  golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
197
  golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
198
  golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 
201
  golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
202
  golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
203
  golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 
 
 
 
204
  golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
205
  golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
206
  golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
 
208
  golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
209
  golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
210
  golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
211
+ golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
212
+ golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
213
  golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
214
  google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
215
  google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 
221
  gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
222
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
223
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 
 
224
  gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
225
  gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
226
+ modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
227
+ modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
228
+ modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
229
+ modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
230
+ modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
231
+ modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
232
+ modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
233
+ modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
234
  modernc.org/libc v1.55.2 h1:UN5eoBYrKp1b+gPYx8nZj5H7uxeybvyoQJfvcg+Bqjc=
235
  modernc.org/libc v1.55.2/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
236
  modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
237
  modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
 
 
238
  modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
239
  modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
240
+ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
241
+ modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
242
+ modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
243
+ modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
244
  modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk=
245
  modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
246
+ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
247
+ modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
248
+ modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
249
+ modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
250
  nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
251
  nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
252
  nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
internal/bot/client.go CHANGED
@@ -1,7 +1,7 @@
1
  package bot
2
 
3
  import (
4
- "EverythingSuckz/fsb/config"
5
  "context"
6
  "time"
7
 
 
1
  package bot
2
 
3
  import (
4
+ "TelegramCloud/tgf/config"
5
  "context"
6
  "time"
7
 
internal/bot/helper.go CHANGED
@@ -2,7 +2,7 @@
2
  package bot
3
 
4
  import (
5
- "EverythingSuckz/fsb/internal/utils"
6
  "context"
7
  "fmt"
8
  "math/rand"
 
2
  package bot
3
 
4
  import (
5
+ "TelegramCloud/tgf/internal/utils"
6
  "context"
7
  "fmt"
8
  "math/rand"
internal/bot/refresher.go CHANGED
@@ -2,8 +2,8 @@
2
  package bot
3
 
4
  import (
5
- "EverythingSuckz/fsb/internal/db"
6
- "EverythingSuckz/fsb/internal/utils"
7
  "context"
8
  "sync/atomic"
9
  "time"
 
2
  package bot
3
 
4
  import (
5
+ "TelegramCloud/tgf/internal/db"
6
+ "TelegramCloud/tgf/internal/utils"
7
  "context"
8
  "sync/atomic"
9
  "time"
internal/bot/userbot.go CHANGED
@@ -1,7 +1,7 @@
1
  package bot
2
 
3
  import (
4
- "EverythingSuckz/fsb/config"
5
  "errors"
6
 
7
  "github.com/celestix/gotgproto"
 
1
  package bot
2
 
3
  import (
4
+ "TelegramCloud/tgf/config"
5
  "errors"
6
 
7
  "github.com/celestix/gotgproto"
internal/bot/workers.go CHANGED
@@ -1,7 +1,7 @@
1
  package bot
2
 
3
  import (
4
- "EverythingSuckz/fsb/config"
5
  "context"
6
  "fmt"
7
  "os"
 
1
  package bot
2
 
3
  import (
4
+ "TelegramCloud/tgf/config"
5
  "context"
6
  "fmt"
7
  "os"
internal/cache/cache.go CHANGED
@@ -1,7 +1,7 @@
1
  package cache
2
 
3
  import (
4
- "EverythingSuckz/fsb/internal/types"
5
  "bytes"
6
  "encoding/gob"
7
  "sync"
 
1
  package cache
2
 
3
  import (
4
+ "TelegramCloud/tgf/internal/types"
5
  "bytes"
6
  "encoding/gob"
7
  "sync"
internal/commands/admin.go ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package commands
2
+
3
+ import (
4
+ "TelegramCloud/tgf/config"
5
+ "TelegramCloud/tgf/internal/db"
6
+ "TelegramCloud/tgf/internal/utils"
7
+ "context"
8
+ "fmt"
9
+ "strconv"
10
+ "strings"
11
+
12
+ "github.com/celestix/gotgproto/dispatcher"
13
+ "github.com/celestix/gotgproto/dispatcher/handlers"
14
+ "github.com/celestix/gotgproto/ext"
15
+ "github.com/gotd/td/telegram/message/styling"
16
+ "go.uber.org/zap"
17
+ )
18
+
19
+ func (m *command) LoadAdmin(dispatcher dispatcher.Dispatcher) {
20
+ log := m.log.Named("admin")
21
+ defer log.Sugar().Info("Loaded admin commands")
22
+ dispatcher.AddHandler(handlers.NewCommand("promote", promoteHandler))
23
+ dispatcher.AddHandler(handlers.NewCommand("demote", demoteHandler))
24
+ dispatcher.AddHandler(handlers.NewCommand("ban", banHandler))
25
+ dispatcher.AddHandler(handlers.NewCommand("userinfo", userinfoHandler))
26
+ }
27
+
28
+ func promoteHandler(ctx *ext.Context, u *ext.Update) error {
29
+ log := utils.Logger.Named("promoteHandler")
30
+ adminID := u.EffectiveUser().ID
31
+
32
+ // Check if user is admin
33
+ if !isUserAdmin(adminID) {
34
+ ctx.Reply(u, "❌ You don't have permission to use this command.", nil)
35
+ return dispatcher.EndGroups
36
+ }
37
+
38
+ // Check if database is available
39
+ if db.UserMgr == nil {
40
+ ctx.Reply(u, "❌ Admin commands are currently unavailable. Database not connected.", nil)
41
+ return dispatcher.EndGroups
42
+ }
43
+
44
+ // Parse command arguments
45
+ args := strings.Fields(u.EffectiveMessage.GetMessage())
46
+ if len(args) < 3 {
47
+ helpText := `👑 **Promote User**
48
+
49
+ Usage: ` + "`/promote <user_id> <role>`" + `
50
+
51
+ **Available roles:**
52
+ • ` + "`basic`" + ` - Basic user (default limits)
53
+ • ` + "`premium`" + ` - Premium user (higher limits)
54
+ • ` + "`admin`" + ` - Administrator (unlimited)
55
+
56
+ **Example:**
57
+ ` + "`/promote 123456789 premium`" + ``
58
+
59
+ styledMessage := []styling.StyledTextOption{
60
+ styling.Plain(helpText),
61
+ }
62
+ ctx.Reply(u, styledMessage, nil)
63
+ return dispatcher.EndGroups
64
+ }
65
+
66
+ // Parse user ID
67
+ userID, err := strconv.ParseInt(args[1], 10, 64)
68
+ if err != nil {
69
+ ctx.Reply(u, "❌ Invalid user ID. Please provide a valid numeric user ID.", nil)
70
+ return dispatcher.EndGroups
71
+ }
72
+
73
+ // Parse role
74
+ role := strings.ToLower(args[2])
75
+ if role != db.RoleBasic && role != db.RolePremium && role != db.RoleAdmin {
76
+ ctx.Reply(u, "❌ Invalid role. Available roles: basic, premium, admin", nil)
77
+ return dispatcher.EndGroups
78
+ }
79
+
80
+ // Promote user
81
+ err = db.UserMgr.PromoteUser(context.Background(), userID, role)
82
+ if err != nil {
83
+ log.Error("Failed to promote user", zap.Error(err))
84
+ ctx.Reply(u, "❌ Failed to promote user. Please try again later.", nil)
85
+ return dispatcher.EndGroups
86
+ }
87
+
88
+ // Get updated user stats to show limits
89
+ stats, err := db.UserMgr.GetOrCreateUserStats(context.Background(), userID)
90
+ if err != nil {
91
+ log.Error("Failed to get user stats after promotion", zap.Error(err))
92
+ ctx.Reply(u, fmt.Sprintf("✅ User %d has been promoted to **%s** role.", userID, role), nil)
93
+ return dispatcher.EndGroups
94
+ }
95
+
96
+ roleEmoji := "👤"
97
+ switch role {
98
+ case db.RolePremium:
99
+ roleEmoji = "⭐"
100
+ case db.RoleAdmin:
101
+ roleEmoji = "👑"
102
+ }
103
+
104
+ limitsText := fmt.Sprintf("Daily: %d, Monthly: %d", stats.DailyLimit, stats.MonthlyLimit)
105
+ if role == db.RoleAdmin {
106
+ limitsText = "Unlimited"
107
+ }
108
+
109
+ successText := fmt.Sprintf(`✅ **User Promoted Successfully**
110
+
111
+ 👤 **User ID:** %d
112
+ %s **New Role:** %s
113
+ 📊 **New Limits:** %s
114
+
115
+ The user will see updated limits in their next ` + "`/stats`" + ` command.`,
116
+ userID, roleEmoji, role, limitsText,
117
+ )
118
+
119
+ styledMessage := []styling.StyledTextOption{
120
+ styling.Plain(successText),
121
+ }
122
+ ctx.Reply(u, styledMessage, nil)
123
+ return dispatcher.EndGroups
124
+ }
125
+
126
+ func demoteHandler(ctx *ext.Context, u *ext.Update) error {
127
+ log := utils.Logger.Named("demoteHandler")
128
+ adminID := u.EffectiveUser().ID
129
+
130
+ // Check if user is admin
131
+ if !isUserAdmin(adminID) {
132
+ ctx.Reply(u, "❌ You don't have permission to use this command.", nil)
133
+ return dispatcher.EndGroups
134
+ }
135
+
136
+ // Check if database is available
137
+ if db.UserMgr == nil {
138
+ ctx.Reply(u, "❌ Admin commands are currently unavailable. Database not connected.", nil)
139
+ return dispatcher.EndGroups
140
+ }
141
+
142
+ // Parse command arguments
143
+ args := strings.Fields(u.EffectiveMessage.GetMessage())
144
+ if len(args) < 2 {
145
+ ctx.Reply(u, "Usage: "+"`/demote <user_id>`"+"\n\nThis will demote the user to basic role.", nil)
146
+ return dispatcher.EndGroups
147
+ }
148
+
149
+ // Parse user ID
150
+ userID, err := strconv.ParseInt(args[1], 10, 64)
151
+ if err != nil {
152
+ ctx.Reply(u, "❌ Invalid user ID. Please provide a valid numeric user ID.", nil)
153
+ return dispatcher.EndGroups
154
+ }
155
+
156
+ // Demote user to basic role
157
+ err = db.UserMgr.PromoteUser(context.Background(), userID, db.RoleBasic)
158
+ if err != nil {
159
+ log.Error("Failed to demote user", zap.Error(err))
160
+ ctx.Reply(u, "❌ Failed to demote user. Please try again later.", nil)
161
+ return dispatcher.EndGroups
162
+ }
163
+
164
+ successText := fmt.Sprintf("✅ User %d has been demoted to **basic** role with default limits.", userID)
165
+ ctx.Reply(u, successText, nil)
166
+ return dispatcher.EndGroups
167
+ }
168
+
169
+ func userinfoHandler(ctx *ext.Context, u *ext.Update) error {
170
+ log := utils.Logger.Named("userinfoHandler")
171
+ adminID := u.EffectiveUser().ID
172
+
173
+ // Check if user is admin
174
+ if !isUserAdmin(adminID) {
175
+ ctx.Reply(u, "❌ You don't have permission to use this command.", nil)
176
+ return dispatcher.EndGroups
177
+ }
178
+
179
+ // Check if database is available
180
+ if db.UserMgr == nil {
181
+ ctx.Reply(u, "❌ Admin commands are currently unavailable. Database not connected.", nil)
182
+ return dispatcher.EndGroups
183
+ }
184
+
185
+ // Parse command arguments
186
+ args := strings.Fields(u.EffectiveMessage.GetMessage())
187
+ if len(args) < 2 {
188
+ ctx.Reply(u, "Usage: "+"`/userinfo <user_id>`"+"\n\nGet detailed information about a user.", nil)
189
+ return dispatcher.EndGroups
190
+ }
191
+
192
+ // Parse user ID
193
+ userID, err := strconv.ParseInt(args[1], 10, 64)
194
+ if err != nil {
195
+ ctx.Reply(u, "❌ Invalid user ID. Please provide a valid numeric user ID.", nil)
196
+ return dispatcher.EndGroups
197
+ }
198
+
199
+ // Get user stats
200
+ stats, err := db.UserMgr.GetOrCreateUserStats(context.Background(), userID)
201
+ if err != nil {
202
+ log.Error("Failed to get user stats", zap.Error(err))
203
+ ctx.Reply(u, "❌ Failed to get user information. Please try again later.", nil)
204
+ return dispatcher.EndGroups
205
+ }
206
+
207
+ // Get today's usage
208
+ today := "2024-01-01" // You might want to use time.Now().Format("2006-01-02")
209
+ dailyUsage, err := db.UserMgr.GetDailyUsage(context.Background(), userID, today)
210
+ if err != nil {
211
+ dailyUsage = &db.DailyUsage{FilesCount: 0, BandwidthUsed: 0}
212
+ }
213
+
214
+ // Format information
215
+ roleEmoji := "👤"
216
+ switch stats.Role {
217
+ case db.RolePremium:
218
+ roleEmoji = "⭐"
219
+ case db.RoleAdmin:
220
+ roleEmoji = "👑"
221
+ }
222
+
223
+ limitsText := fmt.Sprintf("Daily: %d, Monthly: %d", stats.DailyLimit, stats.MonthlyLimit)
224
+ if stats.Role == db.RoleAdmin {
225
+ limitsText = "Unlimited"
226
+ }
227
+
228
+ userInfoText := fmt.Sprintf(`👤 **User Information**
229
+
230
+ 🆔 **User ID:** %d
231
+ %s **Role:** %s
232
+ 📊 **Limits:** %s
233
+
234
+ **Statistics:**
235
+ 📁 Files uploaded: %d
236
+ 💾 Total storage: %s
237
+ 📥 Total downloads: %d
238
+ 🌐 Bandwidth used: %s
239
+
240
+ **Today's Usage:**
241
+ 📁 Files: %d
242
+ 🌐 Bandwidth: %s
243
+
244
+ **Account Info:**
245
+ 📅 Member since: %s
246
+ 🕒 Last active: %s`,
247
+ userID, roleEmoji, stats.Role, limitsText,
248
+ stats.FilesUploaded,
249
+ formatBytes(stats.TotalSize),
250
+ stats.TotalDownloads,
251
+ formatBytes(stats.BandwidthUsed),
252
+ dailyUsage.FilesCount,
253
+ formatBytes(dailyUsage.BandwidthUsed),
254
+ stats.CreatedAt.Format("Jan 2, 2006"),
255
+ stats.LastActive.Format("Jan 2, 15:04"),
256
+ )
257
+
258
+ styledMessage := []styling.StyledTextOption{
259
+ styling.Plain(userInfoText),
260
+ }
261
+ ctx.Reply(u, styledMessage, nil)
262
+ return dispatcher.EndGroups
263
+ }
264
+
265
+ func banHandler(ctx *ext.Context, u *ext.Update) error {
266
+ // This is a placeholder for ban functionality
267
+ // You could implement this by adding a "banned" field to user stats
268
+ ctx.Reply(u, "🚧 Ban functionality is not yet implemented.", nil)
269
+ return dispatcher.EndGroups
270
+ }
271
+
272
+ // isUserAdmin checks if a user ID is an admin
273
+ func isUserAdmin(userID int64) bool {
274
+ // Check against bot owner or admin list from config
275
+ // For now, we'll use a simple check - you can enhance this
276
+ adminUsers := []int64{
277
+ // Add your admin user IDs here
278
+ // config.ValueOf.BotOwnerID, // if you have this in config
279
+ }
280
+
281
+ // If no admin users configured, check if user is in allowed users and assume first one is admin
282
+ if len(adminUsers) == 0 && len(config.ValueOf.AllowedUsers) > 0 {
283
+ return userID == config.ValueOf.AllowedUsers[0]
284
+ }
285
+
286
+ for _, adminID := range adminUsers {
287
+ if userID == adminID {
288
+ return true
289
+ }
290
+ }
291
+ return false
292
+ }
internal/commands/batch.go CHANGED
@@ -3,8 +3,8 @@
3
  package commands
4
 
5
  import (
6
- "EverythingSuckz/fsb/internal/state"
7
- "EverythingSuckz/fsb/internal/utils"
8
  "fmt"
9
  "math/rand"
10
  "strings"
 
3
  package commands
4
 
5
  import (
6
+ "TelegramCloud/tgf/internal/state"
7
+ "TelegramCloud/tgf/internal/utils"
8
  "fmt"
9
  "math/rand"
10
  "strings"
internal/commands/history.go ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package commands
2
+
3
+ import (
4
+ "TelegramCloud/tgf/internal/db"
5
+ "TelegramCloud/tgf/internal/utils"
6
+ "context"
7
+ "fmt"
8
+ "strings"
9
+ "time"
10
+
11
+ "github.com/celestix/gotgproto/dispatcher"
12
+ "github.com/celestix/gotgproto/dispatcher/handlers"
13
+ "github.com/celestix/gotgproto/ext"
14
+ "github.com/gotd/td/telegram/message/styling"
15
+ "github.com/gotd/td/tg"
16
+ "go.uber.org/zap"
17
+ )
18
+
19
+ func (m *command) LoadHistory(dispatcher dispatcher.Dispatcher) {
20
+ log := m.log.Named("history")
21
+ defer log.Sugar().Info("Loaded history command")
22
+ dispatcher.AddHandler(handlers.NewCommand("history", historyHandler))
23
+ }
24
+
25
+ func historyHandler(ctx *ext.Context, u *ext.Update) error {
26
+ log := utils.Logger.Named("historyHandler")
27
+ userID := u.EffectiveUser().ID
28
+
29
+ // Check if database is available
30
+ if db.UserMgr == nil {
31
+ ctx.Reply(u, "❌ History is currently unavailable. Database not connected.", nil)
32
+ return dispatcher.EndGroups
33
+ }
34
+
35
+ // Get user's file history (last 10 files)
36
+ history, err := db.UserMgr.GetUserHistory(context.Background(), userID, 10, 0)
37
+ if err != nil {
38
+ log.Error("Failed to get user history", zap.Error(err))
39
+ ctx.Reply(u, "❌ Failed to retrieve your history. Please try again later.", nil)
40
+ return dispatcher.EndGroups
41
+ }
42
+
43
+ if len(history) == 0 {
44
+ ctx.Reply(u, "📁 Your file history is empty. Upload some files to see them here!", nil)
45
+ return dispatcher.EndGroups
46
+ }
47
+
48
+ // Build history message
49
+ historyText := "📁 **Your Recent Files** (Last 10)\n\n"
50
+
51
+ for i, file := range history {
52
+ // Truncate filename if too long
53
+ displayName := file.FileName
54
+ if len(displayName) > 30 {
55
+ displayName = displayName[:27] + "..."
56
+ }
57
+
58
+ // Format file size
59
+ sizeFormatted := formatBytes(file.FileSize)
60
+
61
+ // Format time
62
+ timeAgo := formatTimeAgo(file.CreatedAt)
63
+
64
+ // Get file type emoji
65
+ emoji := getFileTypeEmoji(file.MimeType)
66
+
67
+ historyText += fmt.Sprintf("`%d.` %s **%s**\n", i+1, emoji, displayName)
68
+ historyText += fmt.Sprintf(" 💾 %s • 📥 %d downloads • %s\n", sizeFormatted, file.DownloadCount, timeAgo)
69
+
70
+ // Add expiration info if applicable
71
+ if file.ExpiresAt != nil {
72
+ if file.ExpiresAt.Before(time.Now()) {
73
+ historyText += " ⚠️ **Expired**\n"
74
+ } else {
75
+ expiresIn := formatTimeAgo(*file.ExpiresAt)
76
+ historyText += fmt.Sprintf(" ⏰ Expires %s\n", expiresIn)
77
+ }
78
+ }
79
+
80
+ historyText += "\n"
81
+ }
82
+
83
+ historyText += "💡 Use `/search <filename>` to find specific files\n"
84
+ historyText += "💡 Use `/settings` to configure file expiration"
85
+
86
+ // Create styled message
87
+ styledMessage := []styling.StyledTextOption{
88
+ styling.Plain(historyText),
89
+ }
90
+
91
+ // Create inline keyboard for navigation
92
+ keyboard := &tg.ReplyInlineMarkup{
93
+ Rows: []tg.KeyboardButtonRow{
94
+ {
95
+ Buttons: []tg.KeyboardButtonClass{
96
+ &tg.KeyboardButtonCallback{Text: "🔄 Refresh", Data: []byte("history_refresh")},
97
+ &tg.KeyboardButtonCallback{Text: "🔍 Search", Data: []byte("history_search")},
98
+ },
99
+ },
100
+ },
101
+ }
102
+
103
+ ctx.Reply(u, styledMessage, &ext.ReplyOpts{Markup: keyboard})
104
+ return dispatcher.EndGroups
105
+ }
106
+
107
+ // getFileTypeEmoji returns appropriate emoji for file type
108
+ func getFileTypeEmoji(mimeType string) string {
109
+ switch {
110
+ case strings.HasPrefix(mimeType, "image/"):
111
+ return "🖼️"
112
+ case strings.HasPrefix(mimeType, "video/"):
113
+ return "🎥"
114
+ case strings.HasPrefix(mimeType, "audio/"):
115
+ return "🎵"
116
+ case strings.Contains(mimeType, "pdf"):
117
+ return "📄"
118
+ case strings.Contains(mimeType, "zip") || strings.Contains(mimeType, "rar") || strings.Contains(mimeType, "7z"):
119
+ return "📦"
120
+ case strings.Contains(mimeType, "text"):
121
+ return "📝"
122
+ default:
123
+ return "📄"
124
+ }
125
+ }
126
+
127
+ // formatTimeAgo formats time duration to human readable string
128
+ func formatTimeAgo(t time.Time) string {
129
+ duration := time.Since(t)
130
+
131
+ if duration < time.Minute {
132
+ return "just now"
133
+ } else if duration < time.Hour {
134
+ minutes := int(duration.Minutes())
135
+ return fmt.Sprintf("%d min ago", minutes)
136
+ } else if duration < 24*time.Hour {
137
+ hours := int(duration.Hours())
138
+ return fmt.Sprintf("%d hr ago", hours)
139
+ } else {
140
+ days := int(duration.Hours() / 24)
141
+ if days == 1 {
142
+ return "1 day ago"
143
+ }
144
+ return fmt.Sprintf("%d days ago", days)
145
+ }
146
+ }
internal/commands/search.go ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package commands
2
+
3
+ import (
4
+ "TelegramCloud/tgf/internal/db"
5
+ "TelegramCloud/tgf/internal/utils"
6
+ "context"
7
+ "fmt"
8
+ "strings"
9
+
10
+ "github.com/celestix/gotgproto/dispatcher"
11
+ "github.com/celestix/gotgproto/dispatcher/handlers"
12
+ "github.com/celestix/gotgproto/ext"
13
+ "github.com/gotd/td/telegram/message/styling"
14
+ "github.com/gotd/td/tg"
15
+ "go.uber.org/zap"
16
+ )
17
+
18
+ func (m *command) LoadSearch(dispatcher dispatcher.Dispatcher) {
19
+ log := m.log.Named("search")
20
+ defer log.Sugar().Info("Loaded search command")
21
+ dispatcher.AddHandler(handlers.NewCommand("search", searchHandler))
22
+ }
23
+
24
+ func searchHandler(ctx *ext.Context, u *ext.Update) error {
25
+ log := utils.Logger.Named("searchHandler")
26
+ userID := u.EffectiveUser().ID
27
+
28
+ // Check if database is available
29
+ if db.UserMgr == nil {
30
+ ctx.Reply(u, "❌ Search is currently unavailable. Database not connected.", nil)
31
+ return dispatcher.EndGroups
32
+ }
33
+
34
+ // Parse command arguments
35
+ args := strings.Fields(u.EffectiveMessage.GetMessage())
36
+ if len(args) < 2 {
37
+ helpText := `🔍 **Search Your Files**
38
+
39
+ Usage: ` + "`/search <filename>`" + `
40
+
41
+ Examples:
42
+ • ` + "`/search video`" + ` - Find files with "video" in name
43
+ • ` + "`/search .pdf`" + ` - Find all PDF files
44
+ • ` + "`/search photo 2024`" + ` - Find files with "photo" and "2024"
45
+
46
+ 💡 Search is case-insensitive and matches partial names.`
47
+
48
+ styledMessage := []styling.StyledTextOption{
49
+ styling.Plain(helpText),
50
+ }
51
+ ctx.Reply(u, styledMessage, nil)
52
+ return dispatcher.EndGroups
53
+ }
54
+
55
+ // Join all arguments after /search as search query
56
+ query := strings.Join(args[1:], " ")
57
+
58
+ // Search user's files
59
+ results, err := db.UserMgr.SearchUserFiles(context.Background(), userID, query, 15)
60
+ if err != nil {
61
+ log.Error("Failed to search user files", zap.Error(err))
62
+ ctx.Reply(u, "❌ Search failed. Please try again later.", nil)
63
+ return dispatcher.EndGroups
64
+ }
65
+
66
+ if len(results) == 0 {
67
+ notFoundText := fmt.Sprintf("🔍 No files found matching: **%s**\n\n💡 Try different keywords or check your file history with "+"`/history`", query)
68
+ styledMessage := []styling.StyledTextOption{
69
+ styling.Plain(notFoundText),
70
+ }
71
+ ctx.Reply(u, styledMessage, nil)
72
+ return dispatcher.EndGroups
73
+ }
74
+
75
+ // Build search results message
76
+ searchText := fmt.Sprintf("🔍 **Search Results for:** %s\n\nFound %d file(s):\n\n", query, len(results))
77
+
78
+ for i, file := range results {
79
+ // Truncate filename if too long
80
+ displayName := file.FileName
81
+ if len(displayName) > 35 {
82
+ displayName = displayName[:32] + "..."
83
+ }
84
+
85
+ // Format file size
86
+ sizeFormatted := formatBytes(file.FileSize)
87
+
88
+ // Format time
89
+ timeAgo := formatTimeAgo(file.CreatedAt)
90
+
91
+ // Get file type emoji
92
+ emoji := getFileTypeEmoji(file.MimeType)
93
+
94
+ searchText += fmt.Sprintf("`%d.` %s **%s**\n", i+1, emoji, displayName)
95
+ searchText += fmt.Sprintf(" 💾 %s • 📥 %d downloads • %s\n", sizeFormatted, file.DownloadCount, timeAgo)
96
+
97
+ // Show hash for regenerating link if needed
98
+ searchText += fmt.Sprintf(" 🔗 Hash: `%s`\n\n", file.ShortHash)
99
+ }
100
+
101
+ if len(results) == 15 {
102
+ searchText += "⚠️ Showing first 15 results. Use more specific keywords for better results."
103
+ }
104
+
105
+ // Create styled message
106
+ styledMessage := []styling.StyledTextOption{
107
+ styling.Plain(searchText),
108
+ }
109
+
110
+ // Create inline keyboard
111
+ keyboard := &tg.ReplyInlineMarkup{
112
+ Rows: []tg.KeyboardButtonRow{
113
+ {
114
+ Buttons: []tg.KeyboardButtonClass{
115
+ &tg.KeyboardButtonCallback{Text: "🔄 Search Again", Data: []byte("search_again")},
116
+ &tg.KeyboardButtonCallback{Text: "📁 View History", Data: []byte("view_history")},
117
+ },
118
+ },
119
+ },
120
+ }
121
+
122
+ ctx.Reply(u, styledMessage, &ext.ReplyOpts{Markup: keyboard})
123
+ return dispatcher.EndGroups
124
+ }
internal/commands/settings.go ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package commands
2
+
3
+ import (
4
+ "TelegramCloud/tgf/internal/db"
5
+ "TelegramCloud/tgf/internal/utils"
6
+ "context"
7
+ "fmt"
8
+ "strings"
9
+
10
+ "github.com/celestix/gotgproto/dispatcher"
11
+ "github.com/celestix/gotgproto/dispatcher/handlers"
12
+ "github.com/celestix/gotgproto/ext"
13
+ "github.com/gotd/td/telegram/message/styling"
14
+ "github.com/gotd/td/tg"
15
+ "go.uber.org/zap"
16
+ )
17
+
18
+ func (m *command) LoadSettings(dispatcher dispatcher.Dispatcher) {
19
+ log := m.log.Named("settings")
20
+ defer log.Sugar().Info("Loaded settings command")
21
+ dispatcher.AddHandler(handlers.NewCommand("settings", settingsHandler))
22
+ // Add callback handler for settings buttons
23
+ dispatcher.AddHandler(handlers.NewCallbackQuery(nil, settingsCallbackHandler))
24
+ }
25
+
26
+ func settingsHandler(ctx *ext.Context, u *ext.Update) error {
27
+ log := utils.Logger.Named("settingsHandler")
28
+ userID := u.EffectiveUser().ID
29
+
30
+ // Check if database is available
31
+ if db.UserMgr == nil {
32
+ ctx.Reply(u, "❌ Settings are currently unavailable. Database not connected.", nil)
33
+ return dispatcher.EndGroups
34
+ }
35
+
36
+ // Get user settings
37
+ settings, err := db.UserMgr.GetUserSettings(context.Background(), userID)
38
+ if err != nil {
39
+ log.Error("Failed to get user settings", zap.Error(err))
40
+ ctx.Reply(u, "❌ Failed to load settings. Please try again later.", nil)
41
+ return dispatcher.EndGroups
42
+ }
43
+
44
+ // Build settings message
45
+ settingsText := buildSettingsMessage(settings)
46
+
47
+ // Create styled message
48
+ styledMessage := []styling.StyledTextOption{
49
+ styling.Plain(settingsText),
50
+ }
51
+
52
+ // Create settings keyboard
53
+ keyboard := createSettingsKeyboard(settings)
54
+
55
+ ctx.Reply(u, styledMessage, &ext.ReplyOpts{Markup: keyboard})
56
+ return dispatcher.EndGroups
57
+ }
58
+
59
+ func settingsCallbackHandler(ctx *ext.Context, u *ext.Update) error {
60
+ log := utils.Logger.Named("settingsCallback")
61
+ userID := u.CallbackQuery.UserID
62
+ data := string(u.CallbackQuery.Data)
63
+
64
+ // Only handle settings-related callbacks
65
+ if !strings.HasPrefix(data, "settings_") {
66
+ return nil // Let other handlers process this
67
+ }
68
+
69
+ // Check if database is available
70
+ if db.UserMgr == nil {
71
+ ctx.Raw.MessagesSetBotCallbackAnswer(ctx, &tg.MessagesSetBotCallbackAnswerRequest{
72
+ QueryID: u.CallbackQuery.QueryID,
73
+ Message: "Settings unavailable",
74
+ Alert: true,
75
+ })
76
+ return dispatcher.EndGroups
77
+ }
78
+
79
+ // Get current settings
80
+ settings, err := db.UserMgr.GetUserSettings(context.Background(), userID)
81
+ if err != nil {
82
+ log.Error("Failed to get user settings", zap.Error(err))
83
+ ctx.Raw.MessagesSetBotCallbackAnswer(ctx, &tg.MessagesSetBotCallbackAnswerRequest{
84
+ QueryID: u.CallbackQuery.QueryID,
85
+ Message: "Failed to load settings",
86
+ Alert: true,
87
+ })
88
+ return dispatcher.EndGroups
89
+ }
90
+
91
+ // Process different settings actions
92
+ switch {
93
+ case data == "settings_expiration":
94
+ // Cycle through expiration options: 0 (never) → 24h → 7 days → 30 days → 0
95
+ switch settings.DefaultExpiration {
96
+ case 0:
97
+ settings.DefaultExpiration = 24 // 24 hours
98
+ case 24:
99
+ settings.DefaultExpiration = 168 // 7 days
100
+ case 168:
101
+ settings.DefaultExpiration = 720 // 30 days
102
+ default:
103
+ settings.DefaultExpiration = 0 // never
104
+ }
105
+
106
+ case data == "settings_notifications":
107
+ settings.EnableNotifications = !settings.EnableNotifications
108
+
109
+ case data == "settings_theme":
110
+ if settings.Theme == "light" {
111
+ settings.Theme = "dark"
112
+ } else {
113
+ settings.Theme = "light"
114
+ }
115
+
116
+ case data == "settings_autodelete":
117
+ settings.AutoDelete = !settings.AutoDelete
118
+
119
+ case data == "settings_language":
120
+ // For now, just toggle between en and other (placeholder)
121
+ if settings.Language == "en" {
122
+ settings.Language = "ru" // placeholder
123
+ } else {
124
+ settings.Language = "en"
125
+ }
126
+
127
+ case data == "settings_close":
128
+ // Delete the settings message
129
+ ctx.DeleteMessages(u.CallbackQuery.UserID, []int{u.CallbackQuery.MsgID})
130
+ ctx.Raw.MessagesSetBotCallbackAnswer(ctx, &tg.MessagesSetBotCallbackAnswerRequest{
131
+ QueryID: u.CallbackQuery.QueryID,
132
+ Message: "Settings closed",
133
+ })
134
+ return dispatcher.EndGroups
135
+
136
+ default:
137
+ ctx.Raw.MessagesSetBotCallbackAnswer(ctx, &tg.MessagesSetBotCallbackAnswerRequest{
138
+ QueryID: u.CallbackQuery.QueryID,
139
+ Message: "Unknown setting",
140
+ Alert: true,
141
+ })
142
+ return dispatcher.EndGroups
143
+ }
144
+
145
+ // Save updated settings
146
+ err = db.UserMgr.UpdateUserSettings(context.Background(), userID, settings)
147
+ if err != nil {
148
+ log.Error("Failed to update user settings", zap.Error(err))
149
+ ctx.Raw.MessagesSetBotCallbackAnswer(ctx, &tg.MessagesSetBotCallbackAnswerRequest{
150
+ QueryID: u.CallbackQuery.QueryID,
151
+ Message: "Failed to save settings",
152
+ Alert: true,
153
+ })
154
+ return dispatcher.EndGroups
155
+ }
156
+
157
+ // Update the message with new settings
158
+ settingsText := buildSettingsMessage(settings)
159
+ keyboard := createSettingsKeyboard(settings)
160
+
161
+ // Edit the message
162
+ peer := ctx.PeerStorage.GetInputPeerById(userID)
163
+ _, err = ctx.Raw.MessagesEditMessage(ctx, &tg.MessagesEditMessageRequest{
164
+ Peer: peer,
165
+ ID: u.CallbackQuery.MsgID,
166
+ Message: settingsText,
167
+ ReplyMarkup: keyboard,
168
+ })
169
+
170
+ if err != nil {
171
+ log.Error("Failed to edit settings message", zap.Error(err))
172
+ }
173
+
174
+ // Answer callback query
175
+ ctx.Raw.MessagesSetBotCallbackAnswer(ctx, &tg.MessagesSetBotCallbackAnswerRequest{
176
+ QueryID: u.CallbackQuery.QueryID,
177
+ Message: "Setting updated",
178
+ })
179
+
180
+ return dispatcher.EndGroups
181
+ }
182
+
183
+ func buildSettingsMessage(settings *db.UserSettings) string {
184
+ expirationText := "Never"
185
+ switch settings.DefaultExpiration {
186
+ case 24:
187
+ expirationText = "24 hours"
188
+ case 168:
189
+ expirationText = "7 days"
190
+ case 720:
191
+ expirationText = "30 days"
192
+ }
193
+
194
+ notificationsText := "✅ Enabled"
195
+ if !settings.EnableNotifications {
196
+ notificationsText = "❌ Disabled"
197
+ }
198
+
199
+ autoDeleteText := "✅ Enabled"
200
+ if !settings.AutoDelete {
201
+ autoDeleteText = "❌ Disabled"
202
+ }
203
+
204
+ themeEmoji := "☀️"
205
+ if settings.Theme == "dark" {
206
+ themeEmoji = "🌙"
207
+ }
208
+
209
+ return fmt.Sprintf(`⚙️ **Your Settings**
210
+
211
+ 🕒 **Default Expiration:** %s
212
+ 📬 **Notifications:** %s
213
+ %s **Theme:** %s
214
+ 🗑️ **Auto Delete:** %s
215
+ 🌍 **Language:** %s
216
+
217
+ 💡 Click buttons below to change settings
218
+ 💡 Changes are saved automatically`,
219
+ expirationText,
220
+ notificationsText,
221
+ themeEmoji, strings.Title(settings.Theme),
222
+ autoDeleteText,
223
+ strings.ToUpper(settings.Language),
224
+ )
225
+ }
226
+
227
+ func createSettingsKeyboard(settings *db.UserSettings) *tg.ReplyInlineMarkup {
228
+ return &tg.ReplyInlineMarkup{
229
+ Rows: []tg.KeyboardButtonRow{
230
+ {
231
+ Buttons: []tg.KeyboardButtonClass{
232
+ &tg.KeyboardButtonCallback{Text: "🕒 Expiration", Data: []byte("settings_expiration")},
233
+ &tg.KeyboardButtonCallback{Text: "📬 Notifications", Data: []byte("settings_notifications")},
234
+ },
235
+ },
236
+ {
237
+ Buttons: []tg.KeyboardButtonClass{
238
+ &tg.KeyboardButtonCallback{Text: "🎨 Theme", Data: []byte("settings_theme")},
239
+ &tg.KeyboardButtonCallback{Text: "🗑️ Auto Delete", Data: []byte("settings_autodelete")},
240
+ },
241
+ },
242
+ {
243
+ Buttons: []tg.KeyboardButtonClass{
244
+ &tg.KeyboardButtonCallback{Text: "🌍 Language", Data: []byte("settings_language")},
245
+ &tg.KeyboardButtonCallback{Text: "❌ Close", Data: []byte("settings_close")},
246
+ },
247
+ },
248
+ },
249
+ }
250
+ }
internal/commands/start.go CHANGED
@@ -3,8 +3,8 @@
3
  package commands
4
 
5
  import (
6
- "EverythingSuckz/fsb/config"
7
- "EverythingSuckz/fsb/internal/utils"
8
  "fmt" // Import fmt
9
  "math/rand"
10
  "strings" // Import strings
 
3
  package commands
4
 
5
  import (
6
+ "TelegramCloud/tgf/config"
7
+ "TelegramCloud/tgf/internal/utils"
8
  "fmt" // Import fmt
9
  "math/rand"
10
  "strings" // Import strings
internal/commands/stats.go ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package commands
2
+
3
+ import (
4
+ "TelegramCloud/tgf/internal/db"
5
+ "TelegramCloud/tgf/internal/utils"
6
+ "context"
7
+ "fmt"
8
+ "time"
9
+
10
+ "github.com/celestix/gotgproto/dispatcher"
11
+ "github.com/celestix/gotgproto/dispatcher/handlers"
12
+ "github.com/celestix/gotgproto/ext"
13
+ "github.com/gotd/td/telegram/message/styling"
14
+ "go.uber.org/zap"
15
+ )
16
+
17
+ func (m *command) LoadStats(dispatcher dispatcher.Dispatcher) {
18
+ log := m.log.Named("stats")
19
+ defer log.Sugar().Info("Loaded stats command")
20
+ dispatcher.AddHandler(handlers.NewCommand("stats", statsHandler))
21
+ }
22
+
23
+ func statsHandler(ctx *ext.Context, u *ext.Update) error {
24
+ log := utils.Logger.Named("statsHandler")
25
+ userID := u.EffectiveUser().ID
26
+
27
+ // Check if database is available
28
+ if db.UserMgr == nil {
29
+ ctx.Reply(u, "❌ Statistics are currently unavailable. Database not connected.", nil)
30
+ return dispatcher.EndGroups
31
+ }
32
+
33
+ // Get user stats
34
+ stats, err := db.UserMgr.GetOrCreateUserStats(context.Background(), userID)
35
+ if err != nil {
36
+ log.Error("Failed to get user stats", zap.Error(err))
37
+ ctx.Reply(u, "❌ Failed to retrieve your statistics. Please try again later.", nil)
38
+ return dispatcher.EndGroups
39
+ }
40
+
41
+ // Get today's usage
42
+ today := time.Now().Format("2006-01-02")
43
+ dailyUsage, err := db.UserMgr.GetDailyUsage(context.Background(), userID, today)
44
+ if err != nil {
45
+ log.Error("Failed to get daily usage", zap.Error(err))
46
+ dailyUsage = &db.DailyUsage{FilesCount: 0, BandwidthUsed: 0}
47
+ }
48
+
49
+ // Format file sizes
50
+ totalSizeFormatted := formatBytes(stats.TotalSize)
51
+ bandwidthFormatted := formatBytes(stats.BandwidthUsed)
52
+ dailyBandwidthFormatted := formatBytes(dailyUsage.BandwidthUsed)
53
+
54
+ // Format role
55
+ roleEmoji := "👤"
56
+ switch stats.Role {
57
+ case db.RolePremium:
58
+ roleEmoji = "⭐"
59
+ case db.RoleAdmin:
60
+ roleEmoji = "👑"
61
+ }
62
+
63
+ // Build stats message
64
+ statsText := fmt.Sprintf(`📊 **Your Statistics**
65
+
66
+ %s **Role:** %s
67
+ 📅 **Member since:** %s
68
+
69
+ 📁 **Files uploaded:** %d
70
+ 💾 **Total storage used:** %s
71
+ 📥 **Total downloads:** %d
72
+ 🌐 **Bandwidth used:** %s
73
+
74
+ **Today's Usage:**
75
+ 📁 Files: %d
76
+ 🌐 Bandwidth: %s
77
+
78
+ **Limits:**`,
79
+ roleEmoji, stats.Role,
80
+ stats.CreatedAt.Format("Jan 2, 2006"),
81
+ stats.FilesUploaded,
82
+ totalSizeFormatted,
83
+ stats.TotalDownloads,
84
+ bandwidthFormatted,
85
+ dailyUsage.FilesCount,
86
+ dailyBandwidthFormatted,
87
+ )
88
+
89
+ // Add limits info
90
+ if stats.Role == db.RoleAdmin {
91
+ statsText += "\n🚀 **Unlimited access**"
92
+ } else {
93
+ dailyRemaining := stats.DailyLimit - int64(dailyUsage.FilesCount)
94
+ if dailyRemaining < 0 {
95
+ dailyRemaining = 0
96
+ }
97
+
98
+ statsText += fmt.Sprintf(`
99
+ 📅 Daily: %d/%d (remaining: %d)
100
+ 📆 Monthly: %d files`,
101
+ dailyUsage.FilesCount, stats.DailyLimit, dailyRemaining,
102
+ stats.MonthlyLimit,
103
+ )
104
+ }
105
+
106
+ statsText += fmt.Sprintf(`
107
+
108
+ 🕒 **Last active:** %s`, stats.LastActive.Format("Jan 2, 15:04"))
109
+
110
+ // Create styled message
111
+ styledMessage := []styling.StyledTextOption{
112
+ styling.Plain(statsText),
113
+ }
114
+
115
+ ctx.Reply(u, styledMessage, nil)
116
+ return dispatcher.EndGroups
117
+ }
118
+
119
+ // formatBytes converts bytes to human readable format
120
+ func formatBytes(bytes int64) string {
121
+ const unit = 1024
122
+ if bytes < unit {
123
+ return fmt.Sprintf("%d B", bytes)
124
+ }
125
+ div, exp := int64(unit), 0
126
+ for n := bytes / unit; n >= unit; n /= unit {
127
+ div *= unit
128
+ exp++
129
+ }
130
+ return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
131
+ }
internal/commands/stream.go CHANGED
@@ -3,10 +3,13 @@
3
  package commands
4
 
5
  import (
6
- "EverythingSuckz/fsb/config"
7
- "EverythingSuckz/fsb/internal/bot"
8
- "EverythingSuckz/fsb/internal/state" // <--- ADD THIS IMPORT
9
- "EverythingSuckz/fsb/internal/utils"
 
 
 
10
 
11
  "github.com/celestix/gotgproto/dispatcher"
12
  "github.com/celestix/gotgproto/dispatcher/handlers"
@@ -52,6 +55,19 @@ func sendLink(ctx *ext.Context, u *ext.Update) error {
52
  userID := u.EffectiveUser().ID
53
  chatId := u.EffectiveChat().GetID()
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  // --- THIS IS THE NEW STATEFUL LOGIC ---
56
  if state.GlobalStateManager.IsUserInBatch(userID) {
57
  // User is in batch mode, so we just add the file and confirm.
 
3
  package commands
4
 
5
  import (
6
+ "TelegramCloud/tgf/config"
7
+ "TelegramCloud/tgf/internal/bot"
8
+ "TelegramCloud/tgf/internal/db"
9
+ "TelegramCloud/tgf/internal/state" // <--- ADD THIS IMPORT
10
+ "TelegramCloud/tgf/internal/utils"
11
+ "context"
12
+ "fmt"
13
 
14
  "github.com/celestix/gotgproto/dispatcher"
15
  "github.com/celestix/gotgproto/dispatcher/handlers"
 
55
  userID := u.EffectiveUser().ID
56
  chatId := u.EffectiveChat().GetID()
57
 
58
+ // --- USER LIMITS CHECK ---
59
+ if db.UserMgr != nil {
60
+ canUpload, limitMsg, err := db.UserMgr.CheckUserLimits(context.Background(), userID)
61
+ if err != nil {
62
+ utils.Logger.Error("Failed to check user limits", zap.Error(err))
63
+ // Continue processing even if limit check fails
64
+ } else if !canUpload {
65
+ ctx.Reply(u, fmt.Sprintf("❌ %s\n\nUse `/stats` to see your usage or contact admin for upgrade.", limitMsg), nil)
66
+ return dispatcher.EndGroups
67
+ }
68
+ }
69
+ // --- END USER LIMITS CHECK ---
70
+
71
  // --- THIS IS THE NEW STATEFUL LOGIC ---
72
  if state.GlobalStateManager.IsUserInBatch(userID) {
73
  // User is in batch mode, so we just add the file and confirm.
internal/db/database.go CHANGED
@@ -2,7 +2,7 @@
2
  package db
3
 
4
  import (
5
- "EverythingSuckz/fsb/config"
6
  "context"
7
  "time"
8
 
@@ -17,6 +17,8 @@ var (
17
  Users *mongo.Collection
18
  OTPs *mongo.Collection
19
  Files *mongo.Collection
 
 
20
  )
21
 
22
  // InitDatabase initializes the MongoDB connection.
@@ -49,6 +51,8 @@ func InitDatabase(log *zap.Logger) error {
49
  OTPs = database.Collection("otps")
50
  Files = database.Collection("files")
51
 
 
 
52
 
53
  log.Info("MongoDB connection established successfully.")
54
  return nil
 
2
  package db
3
 
4
  import (
5
+ "TelegramCloud/tgf/config"
6
  "context"
7
  "time"
8
 
 
17
  Users *mongo.Collection
18
  OTPs *mongo.Collection
19
  Files *mongo.Collection
20
+ // User management instance
21
+ UserMgr *UserManager
22
  )
23
 
24
  // InitDatabase initializes the MongoDB connection.
 
51
  OTPs = database.Collection("otps")
52
  Files = database.Collection("files")
53
 
54
+ // Initialize UserManager
55
+ UserMgr = NewUserManager(database, log)
56
 
57
  log.Info("MongoDB connection established successfully.")
58
  return nil
internal/db/models.go CHANGED
@@ -56,4 +56,77 @@ type FileRecord struct {
56
  FullHash string `bson:"fullHash"`
57
  CreatedAt time.Time `bson:"createdAt"`
58
  UpdatedAt time.Time `bson:"updatedAt"`
59
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  FullHash string `bson:"fullHash"`
57
  CreatedAt time.Time `bson:"createdAt"`
58
  UpdatedAt time.Time `bson:"updatedAt"`
59
+ }
60
+
61
+ // UserStats represents user usage statistics
62
+ type UserStats struct {
63
+ ID primitive.ObjectID `bson:"_id,omitempty"`
64
+ UserID int64 `bson:"user_id,unique"`
65
+ FilesUploaded int64 `bson:"files_uploaded"`
66
+ TotalSize int64 `bson:"total_size"` // in bytes
67
+ TotalDownloads int64 `bson:"total_downloads"`
68
+ BandwidthUsed int64 `bson:"bandwidth_used"` // in bytes
69
+ LastActive time.Time `bson:"last_active"`
70
+ CreatedAt time.Time `bson:"created_at"`
71
+ UpdatedAt time.Time `bson:"updated_at"`
72
+ // Usage limits
73
+ DailyLimit int64 `bson:"daily_limit"` // files per day
74
+ MonthlyLimit int64 `bson:"monthly_limit"` // files per month
75
+ Role string `bson:"role"` // "basic", "premium", "admin"
76
+ }
77
+
78
+ // DailyUsage tracks daily usage per user
79
+ type DailyUsage struct {
80
+ ID primitive.ObjectID `bson:"_id,omitempty"`
81
+ UserID int64 `bson:"user_id"`
82
+ Date string `bson:"date"` // YYYY-MM-DD format
83
+ FilesCount int `bson:"files_count"`
84
+ BandwidthUsed int64 `bson:"bandwidth_used"`
85
+ CreatedAt time.Time `bson:"created_at"`
86
+ }
87
+
88
+ // UserSettings represents user preferences
89
+ type UserSettings struct {
90
+ ID primitive.ObjectID `bson:"_id,omitempty"`
91
+ UserID int64 `bson:"user_id,unique"`
92
+ DefaultExpiration int `bson:"default_expiration"` // hours, 0 = never
93
+ EnableNotifications bool `bson:"enable_notifications"`
94
+ Theme string `bson:"theme"` // "light", "dark"
95
+ Language string `bson:"language"`
96
+ AutoDelete bool `bson:"auto_delete"` // auto delete after expiration
97
+ CreatedAt time.Time `bson:"created_at"`
98
+ UpdatedAt time.Time `bson:"updated_at"`
99
+ }
100
+
101
+ // FileHistory tracks user's file operations for history/search
102
+ type FileHistory struct {
103
+ ID primitive.ObjectID `bson:"_id,omitempty"`
104
+ UserID int64 `bson:"user_id"`
105
+ MessageID int `bson:"message_id"`
106
+ FileName string `bson:"file_name"`
107
+ FileSize int64 `bson:"file_size"`
108
+ MimeType string `bson:"mime_type"`
109
+ ShortHash string `bson:"short_hash"`
110
+ FullHash string `bson:"full_hash"`
111
+ DownloadCount int `bson:"download_count"`
112
+ LastAccessed time.Time `bson:"last_accessed"`
113
+ CreatedAt time.Time `bson:"created_at"`
114
+ ExpiresAt *time.Time `bson:"expires_at,omitempty"` // optional expiration
115
+ }
116
+
117
+ // UserRole constants
118
+ const (
119
+ RoleBasic = "basic"
120
+ RolePremium = "premium"
121
+ RoleAdmin = "admin"
122
+ )
123
+
124
+ // Default limits per role
125
+ const (
126
+ BasicDailyLimit = 50
127
+ BasicMonthlyLimit = 1000
128
+ PremiumDailyLimit = 200
129
+ PremiumMonthlyLimit = 5000
130
+ AdminDailyLimit = -1 // unlimited
131
+ AdminMonthlyLimit = -1 // unlimited
132
+ )
internal/db/user_manager.go ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package db
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "time"
7
+
8
+ "go.mongodb.org/mongo-driver/bson"
9
+ "go.mongodb.org/mongo-driver/mongo"
10
+ "go.mongodb.org/mongo-driver/mongo/options"
11
+ "go.uber.org/zap"
12
+ )
13
+
14
+ // UserManager handles user-related database operations
15
+ type UserManager struct {
16
+ statsCollection *mongo.Collection
17
+ usageCollection *mongo.Collection
18
+ settingsCollection *mongo.Collection
19
+ historyCollection *mongo.Collection
20
+ log *zap.Logger
21
+ }
22
+
23
+ // NewUserManager creates a new UserManager instance
24
+ func NewUserManager(database *mongo.Database, log *zap.Logger) *UserManager {
25
+ return &UserManager{
26
+ statsCollection: database.Collection("user_stats"),
27
+ usageCollection: database.Collection("daily_usage"),
28
+ settingsCollection: database.Collection("user_settings"),
29
+ historyCollection: database.Collection("file_history"),
30
+ log: log.Named("UserManager"),
31
+ }
32
+ }
33
+
34
+ // GetOrCreateUserStats gets user stats or creates default ones
35
+ func (um *UserManager) GetOrCreateUserStats(ctx context.Context, userID int64) (*UserStats, error) {
36
+ var stats UserStats
37
+
38
+ err := um.statsCollection.FindOne(ctx, bson.M{"user_id": userID}).Decode(&stats)
39
+ if err != nil {
40
+ if err == mongo.ErrNoDocuments {
41
+ // Create default stats for new user
42
+ stats = UserStats{
43
+ UserID: userID,
44
+ FilesUploaded: 0,
45
+ TotalSize: 0,
46
+ TotalDownloads: 0,
47
+ BandwidthUsed: 0,
48
+ LastActive: time.Now(),
49
+ CreatedAt: time.Now(),
50
+ UpdatedAt: time.Now(),
51
+ DailyLimit: BasicDailyLimit,
52
+ MonthlyLimit: BasicMonthlyLimit,
53
+ Role: RoleBasic,
54
+ }
55
+
56
+ _, err = um.statsCollection.InsertOne(ctx, stats)
57
+ if err != nil {
58
+ return nil, fmt.Errorf("failed to create user stats: %w", err)
59
+ }
60
+ um.log.Info("Created new user stats", zap.Int64("userID", userID))
61
+ } else {
62
+ return nil, fmt.Errorf("failed to get user stats: %w", err)
63
+ }
64
+ }
65
+
66
+ return &stats, nil
67
+ }
68
+
69
+ // UpdateUserStats updates user statistics
70
+ func (um *UserManager) UpdateUserStats(ctx context.Context, userID int64, fileSize int64) error {
71
+ update := bson.M{
72
+ "$inc": bson.M{
73
+ "files_uploaded": 1,
74
+ "total_size": fileSize,
75
+ },
76
+ "$set": bson.M{
77
+ "last_active": time.Now(),
78
+ "updated_at": time.Now(),
79
+ },
80
+ }
81
+
82
+ _, err := um.statsCollection.UpdateOne(
83
+ ctx,
84
+ bson.M{"user_id": userID},
85
+ update,
86
+ )
87
+ if err != nil {
88
+ return fmt.Errorf("failed to update user stats: %w", err)
89
+ }
90
+
91
+ return nil
92
+ }
93
+
94
+ // UpdateDownloadStats updates download statistics
95
+ func (um *UserManager) UpdateDownloadStats(ctx context.Context, userID int64, bandwidth int64) error {
96
+ update := bson.M{
97
+ "$inc": bson.M{
98
+ "total_downloads": 1,
99
+ "bandwidth_used": bandwidth,
100
+ },
101
+ "$set": bson.M{
102
+ "last_active": time.Now(),
103
+ "updated_at": time.Now(),
104
+ },
105
+ }
106
+
107
+ _, err := um.statsCollection.UpdateOne(
108
+ ctx,
109
+ bson.M{"user_id": userID},
110
+ update,
111
+ )
112
+ if err != nil {
113
+ return fmt.Errorf("failed to update download stats: %w", err)
114
+ }
115
+
116
+ return nil
117
+ }
118
+
119
+ // GetDailyUsage gets user's daily usage
120
+ func (um *UserManager) GetDailyUsage(ctx context.Context, userID int64, date string) (*DailyUsage, error) {
121
+ var usage DailyUsage
122
+
123
+ err := um.usageCollection.FindOne(ctx, bson.M{
124
+ "user_id": userID,
125
+ "date": date,
126
+ }).Decode(&usage)
127
+
128
+ if err != nil {
129
+ if err == mongo.ErrNoDocuments {
130
+ // Create new daily usage record
131
+ usage = DailyUsage{
132
+ UserID: userID,
133
+ Date: date,
134
+ FilesCount: 0,
135
+ BandwidthUsed: 0,
136
+ CreatedAt: time.Now(),
137
+ }
138
+
139
+ _, err = um.usageCollection.InsertOne(ctx, usage)
140
+ if err != nil {
141
+ return nil, fmt.Errorf("failed to create daily usage: %w", err)
142
+ }
143
+ } else {
144
+ return nil, fmt.Errorf("failed to get daily usage: %w", err)
145
+ }
146
+ }
147
+
148
+ return &usage, nil
149
+ }
150
+
151
+ // UpdateDailyUsage updates daily usage statistics
152
+ func (um *UserManager) UpdateDailyUsage(ctx context.Context, userID int64, bandwidth int64) error {
153
+ today := time.Now().Format("2006-01-02")
154
+
155
+ update := bson.M{
156
+ "$inc": bson.M{
157
+ "files_count": 1,
158
+ "bandwidth_used": bandwidth,
159
+ },
160
+ }
161
+
162
+ _, err := um.usageCollection.UpdateOne(
163
+ ctx,
164
+ bson.M{"user_id": userID, "date": today},
165
+ update,
166
+ options.Update().SetUpsert(true),
167
+ )
168
+
169
+ if err != nil {
170
+ return fmt.Errorf("failed to update daily usage: %w", err)
171
+ }
172
+
173
+ return nil
174
+ }
175
+
176
+ // CheckUserLimits checks if user has exceeded daily/monthly limits
177
+ func (um *UserManager) CheckUserLimits(ctx context.Context, userID int64) (bool, string, error) {
178
+ stats, err := um.GetOrCreateUserStats(ctx, userID)
179
+ if err != nil {
180
+ return false, "", err
181
+ }
182
+
183
+ // Admin users have unlimited access
184
+ if stats.Role == RoleAdmin {
185
+ return true, "", nil
186
+ }
187
+
188
+ // Check daily limit
189
+ today := time.Now().Format("2006-01-02")
190
+ dailyUsage, err := um.GetDailyUsage(ctx, userID, today)
191
+ if err != nil {
192
+ return false, "", err
193
+ }
194
+
195
+ if stats.DailyLimit > 0 && dailyUsage.FilesCount >= int(stats.DailyLimit) {
196
+ return false, fmt.Sprintf("Daily limit exceeded (%d files). Limit resets at midnight.", stats.DailyLimit), nil
197
+ }
198
+
199
+ // Check monthly limit (simplified - checking last 30 days)
200
+ thirtyDaysAgo := time.Now().AddDate(0, 0, -30).Format("2006-01-02")
201
+ pipeline := []bson.M{
202
+ {"$match": bson.M{
203
+ "user_id": userID,
204
+ "date": bson.M{"$gte": thirtyDaysAgo},
205
+ }},
206
+ {"$group": bson.M{
207
+ "_id": nil,
208
+ "total_files": bson.M{"$sum": "$files_count"},
209
+ }},
210
+ }
211
+
212
+ cursor, err := um.usageCollection.Aggregate(ctx, pipeline)
213
+ if err != nil {
214
+ return false, "", err
215
+ }
216
+ defer cursor.Close(ctx)
217
+
218
+ var result struct {
219
+ TotalFiles int `bson:"total_files"`
220
+ }
221
+
222
+ if cursor.Next(ctx) {
223
+ if err := cursor.Decode(&result); err != nil {
224
+ return false, "", err
225
+ }
226
+
227
+ if stats.MonthlyLimit > 0 && result.TotalFiles >= int(stats.MonthlyLimit) {
228
+ return false, fmt.Sprintf("Monthly limit exceeded (%d files). Upgrade to premium for higher limits.", stats.MonthlyLimit), nil
229
+ }
230
+ }
231
+
232
+ return true, "", nil
233
+ }
234
+
235
+ // GetUserSettings gets user settings or creates default ones
236
+ func (um *UserManager) GetUserSettings(ctx context.Context, userID int64) (*UserSettings, error) {
237
+ var settings UserSettings
238
+
239
+ err := um.settingsCollection.FindOne(ctx, bson.M{"user_id": userID}).Decode(&settings)
240
+ if err != nil {
241
+ if err == mongo.ErrNoDocuments {
242
+ // Create default settings
243
+ settings = UserSettings{
244
+ UserID: userID,
245
+ DefaultExpiration: 0, // never expire
246
+ EnableNotifications: true,
247
+ Theme: "light",
248
+ Language: "en",
249
+ AutoDelete: false,
250
+ CreatedAt: time.Now(),
251
+ UpdatedAt: time.Now(),
252
+ }
253
+
254
+ _, err = um.settingsCollection.InsertOne(ctx, settings)
255
+ if err != nil {
256
+ return nil, fmt.Errorf("failed to create user settings: %w", err)
257
+ }
258
+ } else {
259
+ return nil, fmt.Errorf("failed to get user settings: %w", err)
260
+ }
261
+ }
262
+
263
+ return &settings, nil
264
+ }
265
+
266
+ // UpdateUserSettings updates user settings
267
+ func (um *UserManager) UpdateUserSettings(ctx context.Context, userID int64, settings *UserSettings) error {
268
+ settings.UpdatedAt = time.Now()
269
+
270
+ _, err := um.settingsCollection.UpdateOne(
271
+ ctx,
272
+ bson.M{"user_id": userID},
273
+ bson.M{"$set": settings},
274
+ options.Update().SetUpsert(true),
275
+ )
276
+
277
+ if err != nil {
278
+ return fmt.Errorf("failed to update user settings: %w", err)
279
+ }
280
+
281
+ return nil
282
+ }
283
+
284
+ // AddFileToHistory adds a file to user's history
285
+ func (um *UserManager) AddFileToHistory(ctx context.Context, userID int64, messageID int, fileName string, fileSize int64, mimeType, shortHash, fullHash string) error {
286
+ history := FileHistory{
287
+ UserID: userID,
288
+ MessageID: messageID,
289
+ FileName: fileName,
290
+ FileSize: fileSize,
291
+ MimeType: mimeType,
292
+ ShortHash: shortHash,
293
+ FullHash: fullHash,
294
+ DownloadCount: 0,
295
+ LastAccessed: time.Now(),
296
+ CreatedAt: time.Now(),
297
+ }
298
+
299
+ // Set expiration if user has default expiration set
300
+ settings, err := um.GetUserSettings(ctx, userID)
301
+ if err == nil && settings.DefaultExpiration > 0 {
302
+ expiresAt := time.Now().Add(time.Duration(settings.DefaultExpiration) * time.Hour)
303
+ history.ExpiresAt = &expiresAt
304
+ }
305
+
306
+ _, err = um.historyCollection.InsertOne(ctx, history)
307
+ if err != nil {
308
+ return fmt.Errorf("failed to add file to history: %w", err)
309
+ }
310
+
311
+ return nil
312
+ }
313
+
314
+ // GetUserHistory gets user's file history with pagination
315
+ func (um *UserManager) GetUserHistory(ctx context.Context, userID int64, limit int, offset int) ([]FileHistory, error) {
316
+ opts := options.Find().
317
+ SetSort(bson.D{{"created_at", -1}}).
318
+ SetLimit(int64(limit)).
319
+ SetSkip(int64(offset))
320
+
321
+ cursor, err := um.historyCollection.Find(ctx, bson.M{"user_id": userID}, opts)
322
+ if err != nil {
323
+ return nil, fmt.Errorf("failed to get user history: %w", err)
324
+ }
325
+ defer cursor.Close(ctx)
326
+
327
+ var history []FileHistory
328
+ if err := cursor.All(ctx, &history); err != nil {
329
+ return nil, fmt.Errorf("failed to decode user history: %w", err)
330
+ }
331
+
332
+ return history, nil
333
+ }
334
+
335
+ // SearchUserFiles searches user's files by name
336
+ func (um *UserManager) SearchUserFiles(ctx context.Context, userID int64, query string, limit int) ([]FileHistory, error) {
337
+ filter := bson.M{
338
+ "user_id": userID,
339
+ "file_name": bson.M{"$regex": query, "$options": "i"},
340
+ }
341
+
342
+ opts := options.Find().
343
+ SetSort(bson.D{{"created_at", -1}}).
344
+ SetLimit(int64(limit))
345
+
346
+ cursor, err := um.historyCollection.Find(ctx, filter, opts)
347
+ if err != nil {
348
+ return nil, fmt.Errorf("failed to search user files: %w", err)
349
+ }
350
+ defer cursor.Close(ctx)
351
+
352
+ var files []FileHistory
353
+ if err := cursor.All(ctx, &files); err != nil {
354
+ return nil, fmt.Errorf("failed to decode search results: %w", err)
355
+ }
356
+
357
+ return files, nil
358
+ }
359
+
360
+ // UpdateFileDownloadCount increments download count for a file
361
+ func (um *UserManager) UpdateFileDownloadCount(ctx context.Context, userID int64, shortHash string) error {
362
+ update := bson.M{
363
+ "$inc": bson.M{"download_count": 1},
364
+ "$set": bson.M{"last_accessed": time.Now()},
365
+ }
366
+
367
+ _, err := um.historyCollection.UpdateOne(
368
+ ctx,
369
+ bson.M{"user_id": userID, "short_hash": shortHash},
370
+ update,
371
+ )
372
+
373
+ if err != nil {
374
+ return fmt.Errorf("failed to update download count: %w", err)
375
+ }
376
+
377
+ return nil
378
+ }
379
+
380
+ // PromoteUser promotes a user to premium or admin
381
+ func (um *UserManager) PromoteUser(ctx context.Context, userID int64, role string) error {
382
+ var dailyLimit, monthlyLimit int64
383
+
384
+ switch role {
385
+ case RolePremium:
386
+ dailyLimit = PremiumDailyLimit
387
+ monthlyLimit = PremiumMonthlyLimit
388
+ case RoleAdmin:
389
+ dailyLimit = AdminDailyLimit
390
+ monthlyLimit = AdminMonthlyLimit
391
+ default:
392
+ dailyLimit = BasicDailyLimit
393
+ monthlyLimit = BasicMonthlyLimit
394
+ role = RoleBasic
395
+ }
396
+
397
+ update := bson.M{
398
+ "$set": bson.M{
399
+ "role": role,
400
+ "daily_limit": dailyLimit,
401
+ "monthly_limit": monthlyLimit,
402
+ "updated_at": time.Now(),
403
+ },
404
+ }
405
+
406
+ _, err := um.statsCollection.UpdateOne(
407
+ ctx,
408
+ bson.M{"user_id": userID},
409
+ update,
410
+ )
411
+
412
+ if err != nil {
413
+ return fmt.Errorf("failed to promote user: %w", err)
414
+ }
415
+
416
+ return nil
417
+ }
internal/routes/internal_api.go CHANGED
@@ -2,8 +2,8 @@
2
  package routes
3
 
4
  import (
5
- "EverythingSuckz/fsb/config"
6
- "EverythingSuckz/fsb/internal/bot"
7
  "net/http"
8
  "strconv"
9
 
 
2
  package routes
3
 
4
  import (
5
+ "TelegramCloud/tgf/config"
6
+ "TelegramCloud/tgf/internal/bot"
7
  "net/http"
8
  "strconv"
9
 
internal/routes/stream.go CHANGED
@@ -1,8 +1,9 @@
1
  package routes
2
 
3
  import (
4
- "EverythingSuckz/fsb/internal/bot"
5
- "EverythingSuckz/fsb/internal/utils"
 
6
  "fmt"
7
  "io"
8
  "net/http"
@@ -122,6 +123,27 @@ func getStreamRoute(ctx *gin.Context) {
122
 
123
  ctx.Header("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, file.FileName))
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  if r.Method != "HEAD" {
126
  lr, _ := utils.NewTelegramReader(ctx, worker.Client, file.Location, start, end, contentLength, messageID)
127
  if _, err := io.CopyN(w, lr, contentLength); err != nil {
 
1
  package routes
2
 
3
  import (
4
+ "TelegramCloud/tgf/internal/bot"
5
+ "TelegramCloud/tgf/internal/db"
6
+ "TelegramCloud/tgf/internal/utils"
7
  "fmt"
8
  "io"
9
  "net/http"
 
123
 
124
  ctx.Header("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, file.FileName))
125
 
126
+ // Track download in user analytics (if database is available)
127
+ if db.UserMgr != nil {
128
+ go func() {
129
+ // Try to get user ID from file history (we don't have direct user context here)
130
+ // This is a simplified approach - in a real system you might want to include user ID in the URL
131
+ shortHash := utils.GetShortHash(expectedHash)
132
+
133
+ // Update download count for the file
134
+ // Note: We can't easily determine the user ID from just the hash, so this is a limitation
135
+ // You might want to modify the URL structure to include user identification
136
+
137
+ // For now, we'll just log the download
138
+ log.Info("File downloaded",
139
+ zap.String("filename", file.FileName),
140
+ zap.Int64("size", contentLength),
141
+ zap.String("hash", shortHash),
142
+ zap.String("ip", ctx.ClientIP()),
143
+ )
144
+ }()
145
+ }
146
+
147
  if r.Method != "HEAD" {
148
  lr, _ := utils.NewTelegramReader(ctx, worker.Client, file.Location, start, end, contentLength, messageID)
149
  if _, err := io.CopyN(w, lr, contentLength); err != nil {
internal/utils/hashing.go CHANGED
@@ -1,8 +1,8 @@
1
  package utils
2
 
3
  import (
4
- "EverythingSuckz/fsb/config"
5
- "EverythingSuckz/fsb/internal/types"
6
  )
7
 
8
  func PackFile(fileName string, fileSize int64, mimeType string, fileID int64) string {
 
1
  package utils
2
 
3
  import (
4
+ "TelegramCloud/tgf/config"
5
+ "TelegramCloud/tgf/internal/types"
6
  )
7
 
8
  func PackFile(fileName string, fileSize int64, mimeType string, fileID int64) string {
internal/utils/helpers.go CHANGED
@@ -1,10 +1,10 @@
1
  package utils
2
 
3
  import (
4
- "EverythingSuckz/fsb/config"
5
- "EverythingSuckz/fsb/internal/cache"
6
- "EverythingSuckz/fsb/internal/db"
7
- "EverythingSuckz/fsb/internal/types"
8
  "context"
9
  "errors"
10
  "fmt"
@@ -266,7 +266,47 @@ func GenerateStreamLink(ctx *ext.Context, u *ext.Update, messageID int) (string,
266
  hash := GetShortHash(fullHash)
267
  link := fmt.Sprintf("%s/stream/%d?hash=%s", config.ValueOf.Host, newMsgID, hash)
268
 
269
- // *** NEW LOGIC: SAVE THE FILE RECORD FOR THE DASHBOARD ***
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  if db.Files != nil && db.Users != nil {
271
  go func() {
272
  dbCtx := context.Background()
 
1
  package utils
2
 
3
  import (
4
+ "TelegramCloud/tgf/config"
5
+ "TelegramCloud/tgf/internal/cache"
6
+ "TelegramCloud/tgf/internal/db"
7
+ "TelegramCloud/tgf/internal/types"
8
  "context"
9
  "errors"
10
  "fmt"
 
266
  hash := GetShortHash(fullHash)
267
  link := fmt.Sprintf("%s/stream/%d?hash=%s", config.ValueOf.Host, newMsgID, hash)
268
 
269
+ // *** NEW LOGIC: USER MANAGEMENT & ANALYTICS TRACKING ***
270
+ userID := u.EffectiveUser().ID
271
+
272
+ // Check user limits before processing (if database is available)
273
+ if db.UserMgr != nil {
274
+ go func() {
275
+ dbCtx := context.Background()
276
+
277
+ // Check if user has exceeded limits
278
+ canUpload, limitMsg, err := db.UserMgr.CheckUserLimits(dbCtx, userID)
279
+ if err != nil {
280
+ log.Error("Failed to check user limits", zap.Error(err))
281
+ } else if !canUpload {
282
+ log.Warn("User exceeded limits", zap.Int64("userID", userID), zap.String("reason", limitMsg))
283
+ // Note: At this point the file is already processed, but we log the violation
284
+ // You might want to move this check earlier in the process
285
+ }
286
+
287
+ // Update user statistics
288
+ err = db.UserMgr.UpdateUserStats(dbCtx, userID, file.FileSize)
289
+ if err != nil {
290
+ log.Error("Failed to update user stats", zap.Error(err))
291
+ }
292
+
293
+ // Update daily usage
294
+ err = db.UserMgr.UpdateDailyUsage(dbCtx, userID, 0) // 0 bandwidth for upload
295
+ if err != nil {
296
+ log.Error("Failed to update daily usage", zap.Error(err))
297
+ }
298
+
299
+ // Add to user's file history
300
+ err = db.UserMgr.AddFileToHistory(dbCtx, userID, newMsgID, file.FileName, file.FileSize, file.MimeType, hash, fullHash)
301
+ if err != nil {
302
+ log.Error("Failed to add file to user history", zap.Error(err))
303
+ } else {
304
+ log.Info("Successfully tracked user activity", zap.Int64("userID", userID), zap.String("fileName", file.FileName))
305
+ }
306
+ }()
307
+ }
308
+
309
+ // *** EXISTING DASHBOARD LOGIC (keep this as is) ***
310
  if db.Files != nil && db.Users != nil {
311
  go func() {
312
  dbCtx := context.Background()