FIRST commit
This commit is contained in:
commit
3ec9d26c67
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/bin/
|
||||
/.env
|
||||
/k8s/deployment.yml
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
#FROM golang:latest as builder
|
||||
#WORKDIR /app
|
||||
#COPY . .
|
||||
#RUN go mod download && go mod tidy
|
||||
|
||||
#RUN go build -o ./bin/us-dop-bot ./cmd/bot
|
||||
|
||||
FROM debian:unstable-slim
|
||||
ARG BINAME=us-dop-bot-linux-arm64-0.0.0_1
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y ca-certificates
|
||||
|
||||
COPY ./bin/${BINAME} /app/us-dop-bot
|
||||
WORKDIR /app
|
||||
RUN echo "bin name ${BINAME}"
|
||||
# RUN mv /app/${BINAME} /app/us-dop-bot
|
||||
CMD ["/app/us-dop-bot"]
|
||||
66
Makefile
Normal file
66
Makefile
Normal file
@ -0,0 +1,66 @@
|
||||
# must create a .env file with info
|
||||
# must have compose installed
|
||||
include .env
|
||||
export
|
||||
OS:=${shell go env GOOS}
|
||||
ARCH=$(shell go env GOARCH)
|
||||
OOSS="linux"
|
||||
ARRCHS="arm 386"
|
||||
DEBUG=1
|
||||
SERVICE=consulta-c-bot
|
||||
VERSION=0.0.0_1
|
||||
BINAME=$(SERVICE)-$(OS)-$(ARCH)-$(VERSION)
|
||||
BINAMEARM=$(SERVICE)-$(OS)-arm64-$(VERSION)
|
||||
# can be docker or podman or whatever
|
||||
CONTAINERS=docker
|
||||
COMPOSE=$(CONTAINERS)-compose
|
||||
# Configure local registry
|
||||
REGADDR=192.168.0.151:32000
|
||||
K8SRSNAME=$(shell kubectl get rs --no-headers -o custom-columns=":metadata.name" | grep $(SERVICE))
|
||||
.phony: all clean build test clean-image build-image build-image-debug run-image run-image-debug run-local
|
||||
|
||||
|
||||
build-image: build
|
||||
# here we made the images and push to registry with buildx
|
||||
@$(CONTAINERS) buildx build --build-arg="BINAME=$(BINAMEARM)" --platform linux/arm64 --push -t $(REGADDR)/$(SERVICE):latest .
|
||||
|
||||
# Here we upload it to local
|
||||
|
||||
build-test-image:
|
||||
@$(CONTAINERS) buildx build --platform linux/arm64 --push -t $(REGADDR)/$(SERVICE):latest .
|
||||
|
||||
run-image: build-image
|
||||
@$(CONTAINERS) compose -f docker-compose.yaml up
|
||||
|
||||
build-image-debug: clean
|
||||
@$(CONTAINERS) compose -f docker-compose-debug.yaml build
|
||||
|
||||
run-image-debug: build-image-debug
|
||||
@$(CONTAINERS) compose -f docker-compose-debug.yaml up
|
||||
|
||||
run-local:clean build
|
||||
@bin/$(BINAME)
|
||||
debug-local:
|
||||
@dlv debug cmd/bot/main.go
|
||||
build: clean
|
||||
#@mkdir dolardb
|
||||
@env GOOS=$(OS) GOARCH=$(arch) go build --tags "fts5" -o ./bin/$(BINAME) ./cmd/bot/.
|
||||
@env GOOS=$(OS) GOARCH=arm64 go build --tags "fts5" -o ./bin/$(BINAMEARM) ./cmd/bot/.
|
||||
|
||||
create-descriptors:
|
||||
@envsubst < k8s/deployment.yml.template > k8s/deployment.yml
|
||||
|
||||
deploy: build-image create-descriptors
|
||||
@kubectl apply -f k8s/deployment.yml
|
||||
@kubectl scale rs $(K8SRSNAME) --replicas=0
|
||||
@kubectl scale rs $(K8SRSNAME) --replicas=1
|
||||
|
||||
test:
|
||||
@go -count=1 test ./...
|
||||
clean:
|
||||
@rm -rf ./bin
|
||||
|
||||
clean-image:
|
||||
@$(CONTAINERS) system prune -f
|
||||
|
||||
|
||||
101
cmd/bot/main.go
Normal file
101
cmd/bot/main.go
Normal file
@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/config"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/adapter/grpc/cedulados"
|
||||
user "git.maximotejeda.com/maximo/cedulados-bot/internal/adapter/grpc/users"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/api"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgb "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
var (
|
||||
maxWorkers = runtime.GOMAXPROCS(4)
|
||||
sem = semaphore.NewWeighted(2)
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
bot, err := tgb.NewBotAPI(config.GetToken())
|
||||
log := slog.Default()
|
||||
|
||||
if err != nil {
|
||||
log.Error("initiating bot", "error",err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
bot.Debug = true
|
||||
|
||||
log.Info("Authorized on account %s", "username", bot.Self.UserName, "maxWorkers", 2)
|
||||
|
||||
u := tgb.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
|
||||
updates := bot.GetUpdatesChan(u)
|
||||
sign := make(chan int)
|
||||
app := api.NewApi(ctx, log, bot)
|
||||
_, us, cCoon, uConn := CreateAdaptersGRPC()
|
||||
_ = us.CreateBot(bot.Self.UserName)
|
||||
cCoon.Close()
|
||||
uConn.Close()
|
||||
// break if not admin set
|
||||
_ = config.GetAdminsList()
|
||||
for {
|
||||
select {
|
||||
case update := <-updates:
|
||||
if err := sem.Acquire(ctx, 1); err != nil{
|
||||
log.Error("failed to get semaphore", "err", err.Error)
|
||||
bot.Send(tgb.NewMessage(update.Message.Chat.ID, "too may request, Please try again"))
|
||||
continue
|
||||
}
|
||||
go func(){
|
||||
defer sem.Release(1)
|
||||
c, u, cConn, uConn := CreateAdaptersGRPC()
|
||||
app.Run(&update, c, u)
|
||||
defer cConn.Close()
|
||||
defer uConn.Close()
|
||||
}()
|
||||
|
||||
case <-sign:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func CreateAdaptersGRPC() (ports.CeduladosService, ports.UserService, *grpc.ClientConn, *grpc.ClientConn) {
|
||||
log := slog.Default()
|
||||
// we are outside update so we will be querying db to
|
||||
// get users interested in specific updates ex bpd, brd, apa
|
||||
// userID inst=> comma separated string
|
||||
var opts []grpc.DialOption
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
cedConn, err := grpc.NewClient(config.GetCeduladosServiceURL(), opts...)
|
||||
if err != nil {
|
||||
log.Error("creating gerpc conn", "error", err)
|
||||
panic(err)
|
||||
}
|
||||
userConn, err := grpc.NewClient(config.GetUserServiceURL(), opts...)
|
||||
if err != nil {
|
||||
log.Error("creating gerpc conn", "error", err)
|
||||
panic(err)
|
||||
}
|
||||
ced, err := cedulados.NewAdapter(cedConn)
|
||||
if err != nil {
|
||||
log.Error("creating service adapter", "error", err)
|
||||
panic(err)
|
||||
}
|
||||
user, err := user.NewAdapter(userConn)
|
||||
if err != nil {
|
||||
log.Error("creating service adapter", "error", err)
|
||||
panic(err)
|
||||
}
|
||||
return ced, user, cedConn, userConn
|
||||
}
|
||||
38
config/config.go
Normal file
38
config/config.go
Normal file
@ -0,0 +1,38 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func GetToken() string {
|
||||
return getEnvVariable("BOT_TOKEN")
|
||||
}
|
||||
|
||||
func GetNatsURI() string {
|
||||
return getEnvVariable("NATS_SERVICE_URL")
|
||||
}
|
||||
|
||||
func GetCeduladosServiceURL() string {
|
||||
return getEnvVariable("CEDULADOS_SERVICE_URL")
|
||||
}
|
||||
|
||||
func GetUserServiceURL() string {
|
||||
return getEnvVariable("TGBUSER_SERVICE_URL")
|
||||
}
|
||||
|
||||
func GetEnvironment() string {
|
||||
return getEnvVariable("ENV")
|
||||
}
|
||||
|
||||
func GetAdminsList()string {
|
||||
return getEnvVariable("ADMINS")
|
||||
}
|
||||
|
||||
func getEnvVariable(key string) string {
|
||||
if os.Getenv(key) == "" {
|
||||
log.Fatal("error getting key ", key)
|
||||
}
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
19
go.mod
Normal file
19
go.mod
Normal file
@ -0,0 +1,19 @@
|
||||
module git.maximotejeda.com/maximo/cedulados-bot
|
||||
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
git.maximotejeda.com/maximo/cedulados v0.0.2
|
||||
git.maximotejeda.com/maximo/tgb-user v0.0.5
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/text v0.21.0
|
||||
google.golang.org/grpc v1.69.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
)
|
||||
38
go.sum
Normal file
38
go.sum
Normal file
@ -0,0 +1,38 @@
|
||||
git.maximotejeda.com/maximo/cedulados v0.0.1 h1:00ghxI7KnTS/kIjpt0kX8co84YoMTCmUqZMRphrF/Qk=
|
||||
git.maximotejeda.com/maximo/cedulados v0.0.1/go.mod h1:LngBMRNqF+CCqPaK3wvLs8gvJZgfMqPTYuEPZy9nOPM=
|
||||
git.maximotejeda.com/maximo/cedulados v0.0.2 h1:xGAUfy6UXPV3nOrW+12JUWCXZrEDkb1kIEZPk+1Ov+o=
|
||||
git.maximotejeda.com/maximo/cedulados v0.0.2/go.mod h1:LngBMRNqF+CCqPaK3wvLs8gvJZgfMqPTYuEPZy9nOPM=
|
||||
git.maximotejeda.com/maximo/tgb-user v0.0.5 h1:OTACcjzOld9TsQHqzDqGXgdN3CqWrZ2p1ro0mUErCR0=
|
||||
git.maximotejeda.com/maximo/tgb-user v0.0.5/go.mod h1:7KpTUAnwap6cp5pHRKgJygxrN3rftAdTkpCG2zJIpYI=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI=
|
||||
google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
|
||||
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
228
internal/adapter/chat/chat.go
Normal file
228
internal/adapter/chat/chat.go
Normal file
@ -0,0 +1,228 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/config"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/adapter/grpc/cedulados"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/adapter/helper"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgb "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"golang.org/x/text/runes"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
var ChatPool *sync.Pool
|
||||
|
||||
type ChatObj struct {
|
||||
ctx context.Context
|
||||
update *tgb.Update
|
||||
bot *tgb.BotAPI
|
||||
u ports.UserService
|
||||
c ports.CeduladosService
|
||||
}
|
||||
|
||||
func NewChatObj(bot *tgb.BotAPI, updt *tgb.Update) (chat *ChatObj) {
|
||||
if ChatPool == nil {
|
||||
ChatPool = &sync.Pool{
|
||||
New: func() any { return &ChatObj{} },
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
ChatPool.Put(ChatPool.New())
|
||||
}
|
||||
} else {
|
||||
fmt.Println("alredy populated")
|
||||
}
|
||||
|
||||
chat = ChatPool.Get().(*ChatObj)
|
||||
chat.update = updt
|
||||
chat.bot = bot
|
||||
return chat
|
||||
}
|
||||
|
||||
func (ch *ChatObj)Empty() {
|
||||
ch.update = nil
|
||||
ch.c = nil
|
||||
ch.u = nil
|
||||
ChatPool.Put(ch)
|
||||
}
|
||||
|
||||
func (ch *ChatObj)Send(){
|
||||
|
||||
}
|
||||
|
||||
func (ch *ChatObj)Handler( cSVC ports.CeduladosService, uSVC ports.UserService) {
|
||||
text := NormalizeText(ch.update.Message.Text)
|
||||
textList := strings.Split(text, " ")
|
||||
msg := tgb.NewMessage(ch.update.Message.Chat.ID, "")
|
||||
if len(textList) >= 1 && MessageChecker(text) == "digit" {
|
||||
// in case of message match a cedula
|
||||
ced, err := helper.NewCedula(ch.ctx, text)
|
||||
if err != nil {
|
||||
msg.Text = "cedula no reconocida " + err.Error()
|
||||
} else {
|
||||
msg, photoMsg := ProcessByCedula(ch.ctx, cSVC, uSVC, ced)
|
||||
msg.ChatID = ch.update.Message.Chat.ID
|
||||
if photoMsg != nil {
|
||||
photoMsg.ChatID = ch.update.Message.Chat.ID
|
||||
ch.bot.Send(photoMsg)
|
||||
}
|
||||
ch.bot.Send(msg)
|
||||
return
|
||||
}
|
||||
} else if len(textList) >= 2 && MessageChecker(text) == "word" {
|
||||
msg := ProcessByName(ch.ctx, cSVC, uSVC, textList)
|
||||
msg.ChatID = ch.update.Message.Chat.ID
|
||||
ch.bot.Send(msg)
|
||||
}
|
||||
|
||||
msg.ReplyToMessageID = ch.update.Message.MessageID
|
||||
ch.bot.Send(msg)
|
||||
}
|
||||
|
||||
// ProcessByCedula
|
||||
//
|
||||
// When a text arrives the chat if it match a cedula try to query db
|
||||
func ProcessByCedula(ctx context.Context, cSVC ports.CeduladosService, uSVC ports.UserService, ced *domain.Cedula) (message *tgb.MessageConfig, fotoMsg *tgb.PhotoConfig) {
|
||||
msg := tgb.NewMessage(0, "")
|
||||
message = &msg
|
||||
info , err := cSVC.CeduladoByCedula(context.Background(), ced)
|
||||
if err != nil {
|
||||
fmt.Println("error on query ", err)
|
||||
}
|
||||
|
||||
fmt.Println("!!!!success on query", info)
|
||||
|
||||
if info != nil {
|
||||
msg.Text = fmt.Sprintf(`
|
||||
Nombres: %s
|
||||
Apellidos: %s %s
|
||||
Cedula: %s
|
||||
Direccion: %s
|
||||
Telefono: %s
|
||||
`,
|
||||
strings.ToLower(info.Nombres), strings.ToLower(info.Apellido1), strings.ToLower(info.Apellido2),
|
||||
info.MunCed+info.SeqCed+info.VerCed, RemoveSpaces(info.Direccion), info.Telefono)
|
||||
}
|
||||
var opts []grpc.DialOption
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
cedConn, err := grpc.NewClient(config.GetCeduladosServiceURL(), opts...)
|
||||
if err != nil {
|
||||
|
||||
panic(err)
|
||||
}
|
||||
c, err := cedulados.NewAdapter(cedConn)
|
||||
if err != nil {
|
||||
|
||||
panic(err)
|
||||
}
|
||||
defer cedConn.Close()
|
||||
|
||||
foto, err := c.QueryFotoByID(ctx, 1)
|
||||
if err != nil {
|
||||
fmt.Println("Photo not found", err.Error())
|
||||
return
|
||||
}
|
||||
if foto != nil {
|
||||
rq := tgb.FileReader{Name: fmt.Sprintf("%s-%s-%s", ced.MunCed, ced.SeqCed, ced.VerCed), Reader: bytes.NewReader(foto.Imagen)}
|
||||
fotost := tgb.NewPhoto(msg.ChatID, rq)
|
||||
fotoMsg = &fotost
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ProcessByName(ctx context.Context, cSVC ports.CeduladosService, uSVC ports.UserService, nameList []string) (message *tgb.MessageConfig) {
|
||||
var err error
|
||||
page := int64(0)
|
||||
// look for if the last part of the list is a number
|
||||
lastItem := nameList[len(nameList)-1]
|
||||
|
||||
if MessageChecker(lastItem) == "digit" && !strings.HasPrefix(lastItem, "0") {
|
||||
pageInt, err := strconv.ParseInt(lastItem, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
nameList = nameList[:len(nameList)-1]
|
||||
if pageInt < 20 {
|
||||
page = pageInt
|
||||
}
|
||||
}
|
||||
rows := &domain.MultipleResults{}
|
||||
message = &tgb.MessageConfig{}
|
||||
text := strings.Join(nameList, " ")
|
||||
|
||||
res, err := cSVC.CeduladoByFTS(ctx, "maximo tejeda", 0)
|
||||
if err != nil {
|
||||
fmt.Println("error oon fts", err)
|
||||
}
|
||||
fmt.Println("sucess", res)
|
||||
|
||||
rows, err = cSVC.CeduladoByFTS(ctx, text, page)
|
||||
if err != nil {
|
||||
message.Text = "no hubo resultados para la busqueda"
|
||||
return
|
||||
}
|
||||
|
||||
textToSend := fmt.Sprintf(`
|
||||
Busqueda:
|
||||
Texto: %s
|
||||
Pagina: %d
|
||||
resultados desde: %d A %d
|
||||
De un total de: %d
|
||||
|
||||
`,
|
||||
text,
|
||||
(rows.Page),
|
||||
(rows.Page*10)-9, (rows.Page)*10,
|
||||
rows.Total)
|
||||
for _, v := range rows.Data {
|
||||
textToSend = textToSend + fmt.Sprintf("\n %s %s %s \n %s-%s-%s\n", strings.ToLower(v.Nombres), strings.ToLower(v.Apellido1), strings.ToLower(v.Apellido2), v.MunCed, v.SeqCed, v.VerCed)
|
||||
}
|
||||
message.Text = textToSend
|
||||
return
|
||||
}
|
||||
|
||||
func MessageChecker(text string) string {
|
||||
// Check is text start with number
|
||||
checkSpace := regexp.MustCompile(`^[\s]+.*`)
|
||||
checkNumber := regexp.MustCompile(`^[\d]+.*`)
|
||||
checkWord := regexp.MustCompile(`^[a-zA-Z% ]+.*`)
|
||||
|
||||
if checkNumber.MatchString(text) {
|
||||
return "digit"
|
||||
} else if checkWord.MatchString(text) {
|
||||
return "word"
|
||||
} else if checkSpace.MatchString(text) {
|
||||
t := strings.TrimSpace(text)
|
||||
return MessageChecker(t)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NormalizeText
|
||||
// remove foreign accent
|
||||
func NormalizeText(text string) string {
|
||||
t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
|
||||
result, _, _ := transform.String(t, text)
|
||||
return result
|
||||
}
|
||||
|
||||
func RemoveSpaces(text string) (res string) {
|
||||
re := regexp.MustCompile(`[\s]+`)
|
||||
|
||||
res = re.ReplaceAllString(text, " ")
|
||||
return
|
||||
}
|
||||
1
internal/adapter/command/command.go
Normal file
1
internal/adapter/command/command.go
Normal file
@ -0,0 +1 @@
|
||||
package command
|
||||
190
internal/adapter/grpc/cedulados/cedulados.go
Normal file
190
internal/adapter/grpc/cedulados/cedulados.go
Normal file
@ -0,0 +1,190 @@
|
||||
package cedulados
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
ced "git.maximotejeda.com/maximo/cedulados/proto/golang/cedulados"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Adapter
|
||||
type Adapter struct {
|
||||
cedulados ced.QueryCeduladoClient
|
||||
conn *grpc.ClientConn
|
||||
}
|
||||
// NewAdapter
|
||||
func NewAdapter(conn *grpc.ClientConn) (*Adapter, error){
|
||||
client := ced.NewQueryCeduladoClient(conn)
|
||||
if client == nil{
|
||||
fmt.Println("error creating client, cant be nil" )
|
||||
}
|
||||
return &Adapter{cedulados: client, conn: conn}, nil
|
||||
}
|
||||
// CeduladoByCedula
|
||||
func(a Adapter) CeduladoByCedula(ctx context.Context, c *domain.Cedula)(resp *domain.Info, err error){
|
||||
cReq := &ced.QueryByCedulaRequest{
|
||||
Cedula: &ced.Cedula{
|
||||
MunCed: c.MunCed,
|
||||
SeqCed: c.SeqCed,
|
||||
VerCed: c.VerCed,
|
||||
},
|
||||
}
|
||||
r, err := a.cedulados.CeduladosByCedula(ctx, cReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = mapLocalDomain (r.Cedulado)
|
||||
return
|
||||
}
|
||||
// CeduladoGetByNameLastName
|
||||
func(a Adapter) CeduladoGetByNameLastName(ctx context.Context, params domain.Info, page int64)(info *domain.MultipleResults, err error){
|
||||
cReq := &ced.QueryByNameLastNameRequest{
|
||||
Name: params.Nombres,
|
||||
Apellido_1: params.Apellido1,
|
||||
Apellido_2: params.Apellido2,
|
||||
Page: page,
|
||||
}
|
||||
r, err := a.cedulados.CeduladosByNameLastName(ctx, cReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = &domain.MultipleResults{
|
||||
Page: r.Page,
|
||||
Total: r.Total,
|
||||
}
|
||||
for _, val := range r.Cedulados{
|
||||
info.Data = append(info.Data, mapLocalDomain (val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CeduladoByFTS
|
||||
func(a Adapter) CeduladoByFTS(ctx context.Context, params string, page int64)(info *domain.MultipleResults, err error){
|
||||
cReq := &ced.QueryByFTSRequest{
|
||||
Name: params,
|
||||
Page: page,
|
||||
}
|
||||
r, err := a.cedulados.CeduladosByFTS(ctx, cReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = &domain.MultipleResults{
|
||||
Page: r.Page,
|
||||
Total: r.Total,
|
||||
}
|
||||
for _, val := range r.Cedulados{
|
||||
info.Data = append(info.Data, mapLocalDomain ( val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CeduladoByNameAndLocation
|
||||
func(a Adapter) CeduladoByNameAndLocation(ctx context.Context, params domain.Info, page int64, municipio string)(info *domain.MultipleResults, err error){
|
||||
cReq := &ced.QueryByNamesLocationRequest{
|
||||
Name: params.Nombres,
|
||||
Apellido_1: params.Apellido1,
|
||||
Apellido_2: params.Apellido2,
|
||||
Municipio: municipio,
|
||||
Page: page,
|
||||
}
|
||||
r, err := a.cedulados.CeduladosByNameAndLocation(ctx, cReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = &domain.MultipleResults{
|
||||
Page: r.Page,
|
||||
Total: r.Total,
|
||||
}
|
||||
for _, val := range r.Cedulados{
|
||||
info.Data = append(info.Data, mapLocalDomain ( val))
|
||||
}
|
||||
return
|
||||
}
|
||||
// QueryFotoByCedula
|
||||
func(a Adapter) QueryFotoByCedula(ctx context.Context, c *domain.Cedula)(info *domain.Foto, err error){
|
||||
cReq := &ced.QueryFotoByCedulaRequest{
|
||||
Cedula: &ced.Cedula{
|
||||
MunCed: c.MunCed,
|
||||
SeqCed: c.SeqCed,
|
||||
VerCed: c.VerCed,
|
||||
},
|
||||
}
|
||||
|
||||
r, err := a.cedulados.QueryFotoByCedula(ctx, cReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// here should be a photo
|
||||
info = &domain.Foto{}
|
||||
info.Imagen = r.Foto.Foto
|
||||
info.MunCed = c.MunCed
|
||||
info.SeqCed = c.SeqCed
|
||||
info.VerCed = c.VerCed
|
||||
info.ID = r.Foto.Id
|
||||
return
|
||||
}
|
||||
// QueryFotoByID
|
||||
func(a Adapter) QueryFotoByID(ctx context.Context, id int64)(info *domain.Foto, err error){
|
||||
r, err := a.cedulados.QueryFotoById(ctx, &ced.QueryFotoByIdRequest{Id: id})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = &domain.Foto{}
|
||||
info.Imagen = r.Foto.Foto
|
||||
info.ID = r.Foto.Id
|
||||
info.MunCed = r.Foto.MunCed
|
||||
info.VerCed = r.Foto.VerCed
|
||||
info.SeqCed = r.Foto.SeqCed
|
||||
return
|
||||
}
|
||||
// QueryFotoAllCedulas
|
||||
func(a Adapter) QueryFotoAllCedulas(ctx context.Context, ceds []*domain.Cedula)(fotos []*domain.Foto, err error){
|
||||
for _, c := range ceds {
|
||||
foto := &domain.Foto{}
|
||||
cReq := &ced.QueryFotoByCedulaRequest{
|
||||
Cedula: &ced.Cedula{
|
||||
MunCed: c.MunCed,
|
||||
SeqCed: c.SeqCed,
|
||||
VerCed: c.VerCed,
|
||||
},
|
||||
}
|
||||
r, err := a.cedulados.QueryFotoByCedula(ctx, cReq)
|
||||
if err != nil {
|
||||
fmt.Println("err on ced: ", c)
|
||||
continue
|
||||
}
|
||||
// here should be a photo
|
||||
foto.Imagen = r.Foto.Foto
|
||||
foto.MunCed = c.MunCed
|
||||
foto.SeqCed = c.SeqCed
|
||||
foto.VerCed = c.VerCed
|
||||
foto.ID = r.Foto.Id
|
||||
fotos = append(fotos, foto)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func mapLocalDomain(info *ced.Cedulado)(ifo *domain.Info){
|
||||
if info == nil {
|
||||
fmt.Println("nil info ")
|
||||
return nil
|
||||
}
|
||||
ifo = &domain.Info{
|
||||
Cedula: domain.Cedula{
|
||||
VerCed: info.Cedula.VerCed,
|
||||
MunCed: info.Cedula.MunCed,
|
||||
SeqCed: info.Cedula.SeqCed,
|
||||
},
|
||||
ID: info.Id,
|
||||
Nombres: info.Nombres,
|
||||
Apellido1: info.Apellido_1,
|
||||
Apellido2: info.Apellido_2,
|
||||
Sexo: info.Sexo,
|
||||
Direccion: info.Direccion,
|
||||
Telefono: info.Telefono,
|
||||
}
|
||||
return
|
||||
}
|
||||
245
internal/adapter/grpc/users/users.go
Normal file
245
internal/adapter/grpc/users/users.go
Normal file
@ -0,0 +1,245 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
"git.maximotejeda.com/maximo/tgb-user/proto/golang/tgbuser"
|
||||
tgb "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Adapter struct {
|
||||
user tgbuser.UserManagerClient
|
||||
conn *grpc.ClientConn
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewAdapter(conn *grpc.ClientConn) (*Adapter, error) {
|
||||
log := slog.Default()
|
||||
log = log.With("location", "user adapter")
|
||||
client := tgbuser.NewUserManagerClient(conn)
|
||||
return &Adapter{user: client, conn: conn, log: log}, nil
|
||||
}
|
||||
func (a *Adapter) Get(tgbid int64) (*domain.User, error) {
|
||||
hr, err := a.user.Get(context.Background(), &tgbuser.GetTGBUserRequest{TgbId: tgbid})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := &domain.User{
|
||||
ID: hr.User.Id,
|
||||
Username: hr.User.Username,
|
||||
FirstName: hr.User.FirstName,
|
||||
LastName: hr.User.LastName,
|
||||
TguID: hr.User.TgbId,
|
||||
Created: hr.User.Created,
|
||||
Edited: hr.User.Edited,
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (a Adapter) Create(user *tgb.User) (b bool, err error) {
|
||||
_, err = a.user.Create(context.Background(), &tgbuser.CreateTGBUserRequest{
|
||||
User: &tgbuser.User{
|
||||
TgbId: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Username: user.UserName,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a Adapter) Edit(user *tgb.User) (b bool, err error) {
|
||||
|
||||
_, err = a.user.Edit(context.Background(), &tgbuser.EditTGBUserRequest{
|
||||
User: &tgbuser.User{
|
||||
Username: user.UserName,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
func (a Adapter) Delete(tgbid int64) (b bool, err error) {
|
||||
_, err = a.user.Delete(context.Background(), &tgbuser.DeleteTGBUserRequest{
|
||||
TgbId: tgbid,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a Adapter) GetBots(tgbid int64) (s []string, err error) {
|
||||
hr, err := a.user.GetBots(context.Background(), &tgbuser.GetBotsTGBUserRequest{
|
||||
TgbId: tgbid,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s = []string{}
|
||||
|
||||
if len(hr.Bots) <= 0 {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
for _, it := range hr.Bots {
|
||||
s = append(s, it.BotName)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (a Adapter) AddBot(tgbid int64, botname string) (b bool, err error) {
|
||||
_, err = a.user.AddBot(context.Background(), &tgbuser.AddBotTGBUserRequest{
|
||||
TgbId: tgbid,
|
||||
BotName: botname,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a Adapter) DeleteBot(tgbid int64, botname string) (b bool, err error) {
|
||||
_, err = a.user.DeleteBot(context.Background(), &tgbuser.DeleteBotTGBUserRequest{
|
||||
TgbId: tgbid,
|
||||
BotName: botname,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a Adapter) GetAllBotsUsers(botname string) ([]*domain.User, error) {
|
||||
users, err := a.user.GetAllBotsUsers(context.Background(), &tgbuser.GetAllBotsUsersRequest{BotName: botname})
|
||||
if err != nil {
|
||||
a.log.Error("get all bots users", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
a.log.Info("users", "result", users)
|
||||
list := []*domain.User{}
|
||||
for _, us := range users.Users {
|
||||
user := &domain.User{
|
||||
ID: us.Id,
|
||||
TguID: us.TgbId,
|
||||
Username: us.Username,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
Created: us.Created,
|
||||
Edited: us.Edited,
|
||||
}
|
||||
list = append(list, user)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (a Adapter) CreateBot(botname string) (error){
|
||||
_, err := a.user.CreateBot(context.Background(), &tgbuser.TGBBotNameRequest{BotName: botname})
|
||||
if err != nil {
|
||||
a.log.Error("creating bot", "error", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a Adapter) CreateAccessRequest(tgbID int64, botName string)(bool, error){
|
||||
req := tgbuser.TGBUserBotNameRequest{
|
||||
TgbId: tgbID,
|
||||
BotName: botName,
|
||||
}
|
||||
r,err := a.user.CreateAccessRequest(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("creating access request", "error", err)
|
||||
return false, err
|
||||
}
|
||||
return r.Response, nil
|
||||
}
|
||||
|
||||
func (a Adapter) GrantAccess(tgbID int64, botName string)(bool, error){
|
||||
req := tgbuser.TGBUserBotNameRequest{
|
||||
TgbId: tgbID,
|
||||
BotName: botName,
|
||||
}
|
||||
r,err := a.user.GrantAccess(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("creating access request", "error", err)
|
||||
return false, err
|
||||
}
|
||||
return r.Response, nil
|
||||
}
|
||||
|
||||
func (a Adapter) GetAllAccessRequest(botName string)(*tgbuser.GetAccessResponse, error){
|
||||
req := tgbuser.TGBBotNameRequest{
|
||||
BotName: botName,
|
||||
}
|
||||
r,err := a.user.GetAllAccessRequest(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("creating access request", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (a Adapter) BanUser(tgbID int64, until int64, botName string)(bool, error){
|
||||
req:= tgbuser.TGBBanUserRequest{
|
||||
TgbId: tgbID,
|
||||
BotName: botName,
|
||||
Until: until,
|
||||
}
|
||||
r, err := a.user.BanUser(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("banning user", "error", err)
|
||||
return false, err
|
||||
}
|
||||
return r.Response, nil
|
||||
}
|
||||
|
||||
func (a Adapter) UnBanUser(tgbID int64, botName string)(bool, error){
|
||||
req:= tgbuser.TGBUserBotNameRequest{
|
||||
TgbId: tgbID,
|
||||
BotName: botName,
|
||||
}
|
||||
r, err := a.user.UnBanUser(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("unbaning user", "error", err)
|
||||
return false, err
|
||||
}
|
||||
return r.Response, nil
|
||||
}
|
||||
|
||||
func (a Adapter) GetAllBannedUsers(botName string)(*tgbuser.GetBanResponse, error){
|
||||
req := tgbuser.TGBBotNameRequest{
|
||||
BotName: botName,
|
||||
}
|
||||
r, err := a.user.GetAllBannedUsers(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("getting all banned users", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (a Adapter)GetAccessRequest(tgbID int64) (*tgbuser.GetAccessResponse, error){
|
||||
req := tgbuser.TGBUserRequest{
|
||||
TgbId: tgbID,
|
||||
}
|
||||
r, err := a.user.GetAccessRequest(context.Background(), &req)
|
||||
if err != nil {
|
||||
a.log.Error("geting access request", "userID", tgbID,"error", err)
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
126
internal/adapter/helper/helpers.go
Normal file
126
internal/adapter/helper/helpers.go
Normal file
@ -0,0 +1,126 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"golang.org/x/text/runes"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// AsserCedulas
|
||||
// check for cedula format
|
||||
// 000-+ 0000000 +- 0
|
||||
func NewCedula(ctx context.Context, ced string)(c *domain.Cedula, err error){
|
||||
c = &domain.Cedula{}
|
||||
if len(ced) <= 0 {
|
||||
return nil, fmt.Errorf("cedula length err lenght:%d", len(ced))
|
||||
}
|
||||
// use Regexp to simplify cases
|
||||
re := regexp.MustCompile(`(?P<munCed>\d{3})[\s-]+(?P<seqCed>\d{7})[\s-]+(?P<verCed>\d{1})$`)
|
||||
result := re.FindStringSubmatch(ced)
|
||||
if len(result) != 4 {
|
||||
return nil, fmt.Errorf("Cedula format Err (%s) is not a good format", ced)
|
||||
}
|
||||
for i, name := range re.SubexpNames() {
|
||||
if i != 0 && name != "" {
|
||||
switch name {
|
||||
case "munCed":
|
||||
c.MunCed = result[i]
|
||||
case "seqCed":
|
||||
c.SeqCed = result[i]
|
||||
case "verCed":
|
||||
c.VerCed = result[i]
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewMultiCedula
|
||||
// create a list of Multiple Cedulas from a string
|
||||
// separated by \n
|
||||
func NewMultiCedula(ctx context.Context, ceds string) (cList []*domain.Cedula, err error){
|
||||
splitedCed := strings.Split(ceds, "\n")
|
||||
for _, ced := range splitedCed{
|
||||
c, err := NewCedula(ctx, ced)
|
||||
if err != nil {
|
||||
fmt.Printf("error querying cedula: %s", err)
|
||||
continue
|
||||
}
|
||||
cList = append(cList, c)
|
||||
}
|
||||
return cList, err
|
||||
}
|
||||
|
||||
// RemoveAccent
|
||||
// helps normalize names in db
|
||||
// https://stackoverflow.com/questions/24588295/go-removing-accents-from-strings
|
||||
func RemoveAccent(str string) string {
|
||||
if str == "" {
|
||||
return ""
|
||||
}
|
||||
t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
|
||||
s, _, _ := transform.String(t, str)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
// RemoveSpaces
|
||||
func RemoveSpaces(text string) (res string) {
|
||||
re := regexp.MustCompile(`[\s]+`)
|
||||
|
||||
res = re.ReplaceAllString(text, " ")
|
||||
return
|
||||
}
|
||||
|
||||
// MessageChecker
|
||||
// Check if message start with a number or letter
|
||||
func MessageChecker(text string) string {
|
||||
// Check is text start with number
|
||||
checkSpace := regexp.MustCompile(`^[\s]+.*`)
|
||||
checkNumber := regexp.MustCompile(`^[\d]+.*`)
|
||||
checkWord := regexp.MustCompile(`^[a-zA-Z% ]+.*`)
|
||||
|
||||
if checkNumber.MatchString(text) {
|
||||
return "digit"
|
||||
} else if checkWord.MatchString(text) {
|
||||
return "word"
|
||||
} else if checkSpace.MatchString(text) {
|
||||
t := strings.TrimSpace(text)
|
||||
return MessageChecker(t)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CreateKeyboard
|
||||
// create keybowrds of two rows of any map[string]string input
|
||||
func CreateKeyboard(data map[string]string) tgbotapi.InlineKeyboardMarkup {
|
||||
// hardcoded models
|
||||
keyboard := tgbotapi.NewInlineKeyboardMarkup()
|
||||
// subbuttons := []tgbot.InlineKeyboardButton{}
|
||||
rows := tgbotapi.NewInlineKeyboardRow()
|
||||
counter := 0
|
||||
for key, val := range data {
|
||||
|
||||
if counter != 0 && counter%3 == 0 {
|
||||
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, rows)
|
||||
rows = tgbotapi.NewInlineKeyboardRow()
|
||||
}
|
||||
rows = append(rows, tgbotapi.NewInlineKeyboardButtonData(key, val))
|
||||
if counter >= len(data)-1 {
|
||||
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, rows)
|
||||
}
|
||||
counter++
|
||||
}
|
||||
return keyboard
|
||||
}
|
||||
1
internal/adapter/query/query.go
Normal file
1
internal/adapter/query/query.go
Normal file
@ -0,0 +1 @@
|
||||
package query
|
||||
59
internal/application/api/api.go
Normal file
59
internal/application/api/api.go
Normal file
@ -0,0 +1,59 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/auth"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/command"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/message"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/query"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type api struct {
|
||||
ctx context.Context
|
||||
log *slog.Logger
|
||||
bot *tgbotapi.BotAPI
|
||||
update *tgbotapi.Update
|
||||
command ports.Tgb
|
||||
message ports.Tgb
|
||||
query ports.Tgb
|
||||
cedulados ports.CeduladosService
|
||||
user ports.UserService
|
||||
|
||||
}
|
||||
|
||||
// NewApi
|
||||
// Create a new instance for api
|
||||
func NewApi(ctx context.Context, log *slog.Logger, bot *tgbotapi.BotAPI) *api {
|
||||
log = log.With("location", "api")
|
||||
return &api{ctx: ctx, log: log, bot: bot}
|
||||
}
|
||||
|
||||
// Run
|
||||
// Start bot user interaction process
|
||||
func (a *api) Run(update *tgbotapi.Update, cedSVC ports.CeduladosService, user ports.UserService) {
|
||||
au := auth.NewAuth(a.ctx, a.log, a.bot, update, update.SentFrom(), user)
|
||||
if !(au.Authenticate()) && !update.Message.IsCommand(){
|
||||
a.log.Error("Authentication failed")
|
||||
return
|
||||
}
|
||||
msg := update.Message
|
||||
if msg != nil { // message is not nil can be a command or a text message
|
||||
if msg.IsCommand() {
|
||||
com := command.Newcommand(a.bot, update, cedSVC, user)
|
||||
com.Handler()
|
||||
// is a command
|
||||
} else if msg.Text != "" {
|
||||
// is a text message
|
||||
message := message.NewMessage(a.bot, update, cedSVC, user)
|
||||
message.Handler()
|
||||
}
|
||||
} else if update.CallbackQuery != nil {
|
||||
// is a cal back
|
||||
qr := query.NewQuery(a.bot, update, cedSVC, user)
|
||||
qr.Handler()
|
||||
}
|
||||
}
|
||||
186
internal/application/auth/auth.go
Normal file
186
internal/application/auth/auth.go
Normal file
@ -0,0 +1,186 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/config"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type Auth struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
update *tgbotapi.Update
|
||||
user *tgbotapi.User
|
||||
uSVC ports.UserService
|
||||
log *slog.Logger
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func NewAuth(ctx context.Context, log *slog.Logger, bot *tgbotapi.BotAPI, update *tgbotapi.Update, user *tgbotapi.User, uSVC ports.UserService) *Auth {
|
||||
a := &Auth{
|
||||
ctx: ctx,
|
||||
log: log,
|
||||
user: user,
|
||||
bot: bot,
|
||||
uSVC: uSVC,
|
||||
update: update,
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Auth) Authenticate() bool {
|
||||
switch IsUserAdmin(a.user.ID) {
|
||||
case true:
|
||||
// theres an user env admin
|
||||
// check if user exist on db
|
||||
_, err := a.uSVC.Get(a.user.ID)
|
||||
if err != nil {
|
||||
a.log.Error("geting user", "error", err)
|
||||
a.log.Debug("user seems to not exists")
|
||||
if strings.Contains(err.Error(), "sql: no rows in result set") {
|
||||
// if user does not exist create it
|
||||
_, err := a.uSVC.Create(a.user)
|
||||
a.log.Debug("creating user")
|
||||
if err != nil {
|
||||
a.log.Error("creating new user for admnin", "err", err)
|
||||
}
|
||||
// add bot to user list
|
||||
_, err = a.uSVC.AddBot(a.user.ID, a.bot.Self.UserName)
|
||||
if err != nil {
|
||||
a.log.Error("Adding bot to admin user list", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
case false:
|
||||
// user is not admin and need to be authorized
|
||||
// check if user is on db
|
||||
_, err := a.uSVC.Get(a.update.SentFrom().ID)
|
||||
if err != nil {
|
||||
// user need auth
|
||||
a.log.Error("user not in db, authorization from an admin is required", "error", err)
|
||||
// add user to manage it
|
||||
|
||||
if strings.Contains(err.Error(), "sql: no rows in result set") {
|
||||
// check if theres an access request from the same user and bot
|
||||
_, err := a.uSVC.Create(a.user)
|
||||
if err != nil {
|
||||
a.log.Error("creating new user", "user", a.user.ID, "error", err)
|
||||
}
|
||||
|
||||
// create access request
|
||||
_, err = a.uSVC.CreateAccessRequest(a.user.ID, a.bot.Self.UserName)
|
||||
if err != nil {
|
||||
a.log.Error("creating access request for ", "user", a.user.ID, "error", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot, err := a.uSVC.GetBots(a.user.ID)
|
||||
if err != nil {
|
||||
a.log.Error("checking bots on user access")
|
||||
}
|
||||
switch HasUserAccess(bot, a.bot.Self.UserName) {
|
||||
case true:
|
||||
return true
|
||||
case false:
|
||||
// check for banned user
|
||||
buser, err := a.uSVC.GetAllBannedUsers(a.bot.Self.UserName)
|
||||
if err != nil {
|
||||
a.log.Error("error querying banned user")
|
||||
}
|
||||
for _, u := range buser.GetBans(){
|
||||
if u.TgbId == a.update.SentFrom().ID{
|
||||
msg := tgbotapi.NewMessage(u.TgbId, "user access is restricted, please ask for permission")
|
||||
a.bot.Send(msg)
|
||||
return false
|
||||
}
|
||||
}
|
||||
ac, err := a.uSVC.GetAccessRequest(a.user.ID)
|
||||
acl := []string{}
|
||||
for _, val := range ac.Access {
|
||||
acl = append(acl, val.BotName)
|
||||
}
|
||||
if slices.Contains(acl, a.bot.Self.UserName) {
|
||||
// create access request
|
||||
a.log.Info("Access Request found returning early", "user", a.user.ID, "error", err)
|
||||
return false
|
||||
} else {
|
||||
|
||||
// create one
|
||||
_, err = a.uSVC.CreateAccessRequest(a.user.ID, a.bot.Self.UserName)
|
||||
if err != nil {
|
||||
a.log.Error("creating access request", "err", err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// get all admins
|
||||
userL, _ := GetAdminFromEnv()
|
||||
// send a mesage to all admins
|
||||
for _, adm := range userL {
|
||||
msg := GenerateAccessRequestMessage(a.update.SentFrom(), adm, a.bot.Self.UserName)
|
||||
a.bot.Send(msg)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAdminFromEnv
|
||||
// will get an env variable that is a list of tgbID comma separated
|
||||
// if the user trying to enter is admin auth the user
|
||||
func GetAdminFromEnv() (adminList []int64, errList []error) {
|
||||
adminsStrList := config.GetAdminsList()
|
||||
list := strings.Split(adminsStrList, ",")
|
||||
adminList = []int64{}
|
||||
errList = []error{}
|
||||
for _, item := range list {
|
||||
adm, err := strconv.ParseInt(item, 10, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("parsing tgb admin id: %s\n err: %w", item, err)
|
||||
fmt.Println(err)
|
||||
errList = append(errList, err)
|
||||
continue
|
||||
}
|
||||
adminList = append(adminList, adm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsUserAdmin
|
||||
// check if userID is admin on bot
|
||||
func IsUserAdmin(userID int64) bool {
|
||||
userL, errl := GetAdminFromEnv()
|
||||
if len(errl) > 0 {
|
||||
fmt.Printf("error no admin in var %v", errl)
|
||||
}
|
||||
return slices.Contains(userL, userID)
|
||||
}
|
||||
|
||||
func HasUserAccess(bots []string, botName string) bool {
|
||||
return slices.Contains(bots, botName)
|
||||
}
|
||||
|
||||
func GenerateAccessRequestMessage(up *tgbotapi.User, adm int64, bn string) *tgbotapi.MessageConfig {
|
||||
txt := fmt.Sprintf(`User %s is requesting access
|
||||
ID: %d
|
||||
FirstName: %s
|
||||
LastName: %s
|
||||
ChatID: %d
|
||||
`, up.UserName, up.ID, up.FirstName, up.LastName, up.ID)
|
||||
msg := tgbotapi.NewMessage(adm, txt)
|
||||
keyboard := tgbotapi.InlineKeyboardMarkup{}
|
||||
row := tgbotapi.NewInlineKeyboardRow()
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Grant", fmt.Sprintf("operation=grant&userID=%d&bot=%s", up.ID, bn)))
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Deny", fmt.Sprintf("operation=deny&userID=%d&bot=%s", up.ID, bn)))
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Ignore", fmt.Sprintf("operation=ignore&userID=%d&bot=%s", up.ID, bn)))
|
||||
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, row)
|
||||
msg.ReplyMarkup = keyboard
|
||||
return &msg
|
||||
}
|
||||
382
internal/application/command/command.go
Normal file
382
internal/application/command/command.go
Normal file
@ -0,0 +1,382 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/auth"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
var CommandPool *sync.Pool
|
||||
|
||||
type command struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
update *tgbotapi.Update
|
||||
msg *tgbotapi.MessageConfig
|
||||
delMSG *tgbotapi.DeleteMessageConfig
|
||||
log *slog.Logger
|
||||
ce ports.CeduladosService
|
||||
uSVC ports.UserService
|
||||
}
|
||||
|
||||
// NewMessage
|
||||
// Factory for query handler
|
||||
func Newcommand(bot *tgbotapi.BotAPI, update *tgbotapi.Update, cSVC ports.CeduladosService, user ports.UserService) *command {
|
||||
if CommandPool == nil {
|
||||
CommandPool = &sync.Pool{
|
||||
New: func() any { return &command{} },
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
CommandPool.Put(CommandPool.New())
|
||||
}
|
||||
}
|
||||
log := slog.Default()
|
||||
log = log.With("function", "command", "chat", update.Message.From.ID, "userid", update.Message.From.ID, "username", update.Message.From.UserName)
|
||||
query := CommandPool.Get().(*command)
|
||||
query.update = update
|
||||
query.bot = bot
|
||||
query.log = log
|
||||
query.ce = cSVC
|
||||
query.uSVC = user
|
||||
return query
|
||||
}
|
||||
|
||||
// Empty
|
||||
// Returns pointer to pool
|
||||
func (c *command) Empty() {
|
||||
c.update = nil
|
||||
c.msg = nil
|
||||
c.log = nil
|
||||
c.ce = nil
|
||||
c.uSVC = nil
|
||||
CommandPool.Put(c)
|
||||
}
|
||||
|
||||
// Send
|
||||
// Process message sending to bot
|
||||
func (c *command) Send() {
|
||||
defer c.Empty()
|
||||
c.bot.Send(c.msg)
|
||||
if c.delMSG != nil {
|
||||
c.bot.Send(c.delMSG)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *command) Handler(){
|
||||
msg := tgbotapi.NewMessage(c.update.Message.From.ID, "")
|
||||
delMSG := tgbotapi.NewDeleteMessage(c.update.Message.From.ID, c.update.Message.MessageID)
|
||||
|
||||
c.msg = &msg
|
||||
c.delMSG = &delMSG
|
||||
command := c.update.Message.Command()
|
||||
|
||||
switch strings.ToLower(command) {
|
||||
case "baned":
|
||||
if !checkUserIsAdmin(c){
|
||||
return
|
||||
}
|
||||
bannedUserCommand(c, msg)
|
||||
case "appeal":
|
||||
appealUserCommand(c, msg)
|
||||
case "pending":
|
||||
if !checkUserIsAdmin(c){
|
||||
return
|
||||
}
|
||||
pendingUserCommand(c, msg)
|
||||
case "users":
|
||||
if !checkUserIsAdmin(c){
|
||||
return
|
||||
}
|
||||
usersCommand(c, msg)
|
||||
case "user":
|
||||
if !checkUserIsAdmin(c){
|
||||
return
|
||||
}
|
||||
userCommand(c)
|
||||
case "userid":
|
||||
if !checkUserIsAdmin(c){
|
||||
return
|
||||
}
|
||||
userIDCommand(c)
|
||||
case "ban":
|
||||
if !checkUserIsAdmin(c){
|
||||
return
|
||||
}
|
||||
banCommand(c, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateAppealRequestMessage(up *tgbotapi.User, adm int64, bn string) *tgbotapi.MessageConfig {
|
||||
txt := fmt.Sprintf(`User %s appeal
|
||||
ID: %d
|
||||
FirstName: %s
|
||||
LastName: %s
|
||||
ChatID: %d
|
||||
`, up.UserName, up.ID, up.FirstName, up.LastName, up.ID)
|
||||
msg := tgbotapi.NewMessage(adm, txt)
|
||||
keyboard := tgbotapi.InlineKeyboardMarkup{}
|
||||
row := tgbotapi.NewInlineKeyboardRow()
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Unban", fmt.Sprintf("operation=unban&userID=%d&bot=%s", up.ID, bn)))
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Delete", fmt.Sprintf("operation=delete&userID=%d&bot=%s", up.ID, bn)))
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Ignore", fmt.Sprintf("operation=ignore&userID=%d&bot=%s", up.ID, bn)))
|
||||
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, row)
|
||||
msg.ReplyMarkup = keyboard
|
||||
return &msg
|
||||
}
|
||||
|
||||
func GenerateUserMessage(up *tgbotapi.User, adm int64, bn string) *tgbotapi.MessageConfig {
|
||||
txt := fmt.Sprintf(`User %s
|
||||
ID: %d
|
||||
FirstName: %s
|
||||
LastName: %s
|
||||
ChatID: %d
|
||||
`, up.UserName, up.ID, up.FirstName, up.LastName, up.ID)
|
||||
msg := tgbotapi.NewMessage(adm, txt)
|
||||
keyboard := tgbotapi.InlineKeyboardMarkup{}
|
||||
row := tgbotapi.NewInlineKeyboardRow()
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("ban", fmt.Sprintf("operation=ban&userID=%d&bot=%s", up.ID, bn)))
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Delete", fmt.Sprintf("operation=delete&userID=%d&bot=%s", up.ID, bn)))
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData("Ignore", fmt.Sprintf("operation=ignore&userID=%d&bot=%s", up.ID, bn)))
|
||||
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, row)
|
||||
msg.ReplyMarkup = keyboard
|
||||
return &msg
|
||||
}
|
||||
|
||||
// checkUserIsAdmin
|
||||
// if user is admin return true else false
|
||||
func checkUserIsAdmin(c *command)bool{
|
||||
if !auth.IsUserAdmin(c.update.Message.From.ID){
|
||||
text := "only admin can execute this command"
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// bannedUserCommand
|
||||
// check for user banned on the bot and send all for revision to admin requesting it
|
||||
func bannedUserCommand(c *command, msg tgbotapi.MessageConfig){
|
||||
buser, err := c.uSVC.GetAllBannedUsers(c.bot.Self.UserName)
|
||||
if err != nil {
|
||||
c.log.Error("error querying banned user")
|
||||
}
|
||||
if len(buser.Bans) <=0{
|
||||
|
||||
msg.Text = "users baned not found"
|
||||
c.bot.Send(msg )
|
||||
return
|
||||
}
|
||||
for _, u := range buser.GetBans(){
|
||||
us, _ := c.uSVC.Get(u.TgbId)
|
||||
tgbUs := tgbotapi.User{
|
||||
ID: us.TguID,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
UserName: us.Username,
|
||||
}
|
||||
// send message to issuer
|
||||
msg := GenerateAppealRequestMessage(&tgbUs, c.update.SentFrom().ID, c.bot.Self.UserName)
|
||||
c.bot.Send(msg )
|
||||
}
|
||||
}
|
||||
|
||||
// appealUserCommand
|
||||
// user banned has the oportunity to appeal a command if is unjustify its banning
|
||||
func appealUserCommand(c *command, msg tgbotapi.MessageConfig){
|
||||
buser, err := c.uSVC.GetAllBannedUsers(c.bot.Self.UserName)
|
||||
if err != nil {
|
||||
c.log.Error("error querying banned user")
|
||||
}
|
||||
if len(buser.GetBans())<= 0{
|
||||
msg.Text = "users baned not found"
|
||||
c.bot.Send(msg )
|
||||
return
|
||||
}
|
||||
for _, u := range buser.GetBans(){
|
||||
adms, _:= auth.GetAdminFromEnv()
|
||||
if u.TgbId == c.update.SentFrom().ID{
|
||||
for _, adm := range adms{
|
||||
// send message to all admins
|
||||
msg := GenerateAppealRequestMessage(c.update.SentFrom(), adm, c.bot.Self.UserName)
|
||||
c.bot.Send(msg )
|
||||
|
||||
}
|
||||
|
||||
c.bot.Send(msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pendingUserCommand
|
||||
// render pending access request for bot
|
||||
func pendingUserCommand(c *command, msg tgbotapi.MessageConfig){
|
||||
pac, _ := c.uSVC.GetAllAccessRequest(c.bot.Self.UserName)
|
||||
if len(pac.Access) <= 0 {
|
||||
msg.Text = "there are no users access request"
|
||||
c.bot.Send(msg )
|
||||
return
|
||||
}
|
||||
for _, ac := range pac.Access {
|
||||
us, err := c.uSVC.Get(ac.TgbId)
|
||||
if err != nil{
|
||||
c.log.Error("geting user", "tgbID", us.TguID)
|
||||
return
|
||||
}
|
||||
tgbUS := tgbotapi.User{
|
||||
UserName: us.Username,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
ID: us.TguID,
|
||||
}
|
||||
msg := auth.GenerateAccessRequestMessage(&tgbUS, c.update.Message.From.ID, c.bot.Self.UserName)
|
||||
c.bot.Send(msg)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// usersCommand
|
||||
// render all users on the bot to ban or delete
|
||||
func usersCommand(c *command, msg tgbotapi.MessageConfig){
|
||||
usL , err :=c.uSVC.GetAllBotsUsers(c.bot.Self.UserName)
|
||||
if err != nil {
|
||||
c.log.Error("geting users from bot")
|
||||
return
|
||||
}
|
||||
if len(usL) <= 0 {
|
||||
msg.Text = "users not found registered"
|
||||
c.bot.Send(msg )
|
||||
return
|
||||
}
|
||||
for _, us := range usL{
|
||||
if auth.IsUserAdmin(us.TguID){
|
||||
continue
|
||||
}
|
||||
tgbUS := tgbotapi.User{
|
||||
UserName: us.Username,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
ID: us.TguID,
|
||||
}
|
||||
msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName)
|
||||
c.bot.Send(msg)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// userCommand
|
||||
// render an user message with querys ban delete
|
||||
func userCommand(c *command){
|
||||
msgTXT := strings.Split(c.update.Message.Text, " ")
|
||||
if len(msgTXT) <= 1{
|
||||
text := "command require username argument"
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
userName := msgTXT[1]
|
||||
usL , err :=c.uSVC.GetAllBotsUsers(c.bot.Self.UserName)
|
||||
if err != nil {
|
||||
c.log.Error("geting users from bot")
|
||||
return
|
||||
}
|
||||
for _, us := range usL{
|
||||
if us.Username == userName{
|
||||
tgbUS := tgbotapi.User{
|
||||
UserName: us.Username,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
ID: us.TguID,
|
||||
}
|
||||
msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName)
|
||||
c.bot.Send(msg)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// userIDCommand
|
||||
// Render a message with user info and querys ban delete
|
||||
func userIDCommand(c *command){
|
||||
msgTXT := strings.Split(c.update.Message.Text, " ")
|
||||
if len(msgTXT) <= 1{
|
||||
text := "command require username argument"
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
userIDSTR := msgTXT[1]
|
||||
userID, err := strconv.ParseInt(userIDSTR, 10, 64)
|
||||
if err != nil {
|
||||
c.log.Error("geting users from bot", "error", err)
|
||||
text := "wrong userID argument "+ err.Error()
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
us , err :=c.uSVC.Get(userID)
|
||||
if err != nil {
|
||||
c.log.Error("geting user from bot", "error", err)
|
||||
text := "ERROR: "+ err.Error()
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
bots , err := c.uSVC.GetBots(us.TguID)
|
||||
if err != nil {
|
||||
c.log.Error("geting bots for user ", "error", err)
|
||||
text := "ERROR: "+ err.Error()
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
if !slices.Contains(bots, c.bot.Self.UserName){
|
||||
c.log.Error("user is not part of bot list")
|
||||
text := "user is not part of bot list"
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
tgbUS := tgbotapi.User{
|
||||
UserName: us.Username,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
ID: us.TguID,
|
||||
}
|
||||
msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName)
|
||||
c.bot.Send(msg)
|
||||
}
|
||||
|
||||
// banCommand
|
||||
// ban an user by its user name as param
|
||||
func banCommand(c *command, msg tgbotapi.MessageConfig){
|
||||
msgTXT := strings.Split(c.update.Message.Text, " ")
|
||||
if len(msgTXT) <= 1{
|
||||
text := "command require username argument"
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
userName := msgTXT[1]
|
||||
usL , err :=c.uSVC.GetAllBotsUsers(c.bot.Self.UserName)
|
||||
if err != nil {
|
||||
c.log.Error("geting users from bot")
|
||||
text := "ERROR: "+ err.Error()
|
||||
c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text))
|
||||
return
|
||||
}
|
||||
for _, us := range usL{
|
||||
if us.Username == userName{
|
||||
tgbUS := tgbotapi.User{
|
||||
UserName: us.Username,
|
||||
FirstName: us.FirstName,
|
||||
LastName: us.LastName,
|
||||
ID: us.TguID,
|
||||
}
|
||||
msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName)
|
||||
c.bot.Send(msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
msg.Text = "user not foun on bot list"
|
||||
c.bot.Send(msg)
|
||||
}
|
||||
7
internal/application/domain/cedula.go
Normal file
7
internal/application/domain/cedula.go
Normal file
@ -0,0 +1,7 @@
|
||||
package domain
|
||||
|
||||
type Cedula struct {
|
||||
MunCed string `json:"mun_ced"`
|
||||
SeqCed string `json:"seq_ced"`
|
||||
VerCed string `json:"ver_ced"`
|
||||
}
|
||||
12
internal/application/domain/foto.go
Normal file
12
internal/application/domain/foto.go
Normal file
@ -0,0 +1,12 @@
|
||||
package domain
|
||||
|
||||
type imagen []byte
|
||||
|
||||
type Foto struct {
|
||||
ID int64 `json:"id"`
|
||||
MunCed string `json:"mun_ced"`
|
||||
SeqCed string `json:"seq_ced"`
|
||||
VerCed string `json:"ver_ced"`
|
||||
Sequencia int64 `json:"sequencia"`
|
||||
Imagen imagen `json:"imagen"`
|
||||
}
|
||||
19
internal/application/domain/info.go
Normal file
19
internal/application/domain/info.go
Normal file
@ -0,0 +1,19 @@
|
||||
package domain
|
||||
|
||||
type Info struct {
|
||||
Cedula
|
||||
ID int64 `json:"id"`
|
||||
Nombres string `json:"nombres"`
|
||||
Apellido1 string `json:"apellido1"`
|
||||
Apellido2 string `json:"apellido2"`
|
||||
Sexo string `json:"sexo"`
|
||||
Direccion string `json:"direccion"`
|
||||
Telefono string `json:"telefono"`
|
||||
FechaNac string `json:"fecha_nac"`
|
||||
}
|
||||
|
||||
type MultipleResults struct {
|
||||
Data []*Info `json:"data"`
|
||||
Total int64 `json:"total"`
|
||||
Page int64 `json:"page"`
|
||||
}
|
||||
13
internal/application/domain/user.go
Normal file
13
internal/application/domain/user.go
Normal file
@ -0,0 +1,13 @@
|
||||
package domain
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
TguID int64
|
||||
Username string `json:"username"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Subs []string
|
||||
Created int64
|
||||
Edited int64
|
||||
Deleted int64
|
||||
}
|
||||
192
internal/application/message/#message.go#
Normal file
192
internal/application/message/#message.go#
Normal file
@ -0,0 +1,192 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/adapter/helper"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
var ChatPool *sync.Pool
|
||||
|
||||
type Message struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
update *tgbotapi.Update
|
||||
msg *tgbotapi.MessageConfig
|
||||
log *slog.Logger
|
||||
ce ports.CeduladosService
|
||||
user ports.UserService
|
||||
}
|
||||
|
||||
// NewMessage
|
||||
// Factory for message handler
|
||||
func NewMessage(bot *tgbotapi.BotAPI, update *tgbotapi.Update, cSVC ports.CeduladosService, user ports.UserService) *Message {
|
||||
if ChatPool == nil {
|
||||
ChatPool = &sync.Pool{
|
||||
New: func() any { return &Message{} },
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
ChatPool.Put(ChatPool.New())
|
||||
}
|
||||
}
|
||||
log := slog.Default()
|
||||
log = log.With("function", "message", "chat", update.Message.Chat.ID, "userid", update.Message.From.ID, "username", update.Message.From.UserName)
|
||||
message := ChatPool.Get().(*Message)
|
||||
message.update = update
|
||||
message.bot = bot
|
||||
message.log = log
|
||||
message.ce = cSVC
|
||||
message.user = user
|
||||
return message
|
||||
}
|
||||
|
||||
// Empty
|
||||
// Returns pointer to pool
|
||||
func (m *Message) Empty() {
|
||||
m.update = nil
|
||||
m.msg = nil
|
||||
m.log = nil
|
||||
m.ce = nil
|
||||
m.user = nil
|
||||
ChatPool.Put(m)
|
||||
}
|
||||
|
||||
// Send
|
||||
// Process message sending to bot
|
||||
func (m *Message) Send() {
|
||||
defer m.Empty()
|
||||
m.bot.Send(m.msg)
|
||||
}
|
||||
|
||||
func (m *Message) Handler(){
|
||||
msg := tgbotapi.NewMessage(m.update.Message.Chat.ID, "")
|
||||
m.msg = &msg
|
||||
text := helper.RemoveAccent(m.update.Message.Text)
|
||||
textList := strings.Split(text, " ")
|
||||
|
||||
if len(textList) >= 1 && helper.MessageChecker(text) == "digit" {
|
||||
// in case of message match a cedula
|
||||
ced, err := helper.NewCedula(context.Background(), text)
|
||||
if err != nil {
|
||||
msg.Text = "cedula no reconocida " + err.Error()
|
||||
} else {
|
||||
msg, photoMsg := ProcessByCedula(context.Background(), m.ce, m.user, ced)
|
||||
msg.ChatID = m.update.Message.Chat.ID
|
||||
if photoMsg != nil {
|
||||
photoMsg.ChatID = m.update.Message.Chat.ID
|
||||
m.bot.Send(photoMsg)
|
||||
}
|
||||
m.bot.Send(msg)
|
||||
return
|
||||
}
|
||||
} else if len(textList) >= 2 && helper.MessageChecker(text) == "word" {
|
||||
msg := ProcessByName(context.Background(), m.ce, m.user, textList)
|
||||
msg.ChatID = m.update.Message.Chat.ID
|
||||
m.bot.Send(msg)
|
||||
}
|
||||
|
||||
msg.ReplyToMessageID = m.update.Message.MessageID
|
||||
m.bot.Send(msg)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ProcessByCedula
|
||||
//
|
||||
// When a text arrives the chat if it match a cedula try to query db
|
||||
func ProcessByCedula(ctx context.Context, cSVC ports.CeduladosService, uSVC ports.UserService, ced *domain.Cedula) (message *tgbotapi.MessageConfig, fotoMsg *tgbotapi.PhotoConfig) {
|
||||
|
||||
msg := tgbotapi.NewMessage(0, "")
|
||||
message = &msg
|
||||
info , err := cSVC.CeduladoByCedula(context.Background(), ced)
|
||||
if err != nil {
|
||||
fmt.Println("error on query ", err)
|
||||
}
|
||||
|
||||
fmt.Println("!!!!success on query", info)
|
||||
|
||||
if info != nil {
|
||||
msg.Text = fmt.Sprintf(`
|
||||
Nombres: %s
|
||||
Apellidos: %s %s
|
||||
Cedula: %s
|
||||
Direccion: %s
|
||||
Telefono: %s
|
||||
`,
|
||||
strings.ToLower(info.Nombres), strings.ToLower(info.Apellido1), strings.ToLower(info.Apellido2),
|
||||
info.MunCed+info.SeqCed+info.VerCed, helper.RemoveSpaces(info.Direccion), info.Telefono)
|
||||
}
|
||||
|
||||
foto, err := cSVC.QueryFotoByCedula(ctx, ced)
|
||||
if err != nil {
|
||||
fmt.Println("Photo not found", err.Error())
|
||||
return
|
||||
}
|
||||
if foto != nil {
|
||||
rq := tgbotapi.FileReader{Name: fmt.Sprintf("%s-%s-%s", ced.MunCed, ced.SeqCed, ced.VerCed), Reader: bytes.NewReader(foto.Imagen)}
|
||||
fotost := tgbotapi.NewPhoto(msg.ChatID, rq)
|
||||
fotoMsg = &fotost
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ProcessByName(ctx context.Context, cSVC ports.CeduladosService, uSVC ports.UserService, nameList []string) (message *tgbotapi.MessageConfig) {
|
||||
var err error
|
||||
page := int64(0)
|
||||
// look for if the last part of the list is a number
|
||||
lastItem := nameList[len(nameList)-1]
|
||||
|
||||
if helper.MessageChecker(lastItem) == "digit" && !strings.HasPrefix(lastItem, "0") {
|
||||
pageInt, err := strconv.ParseInt(lastItem, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
nameList = nameList[:len(nameList)-1]
|
||||
if pageInt < 20 {
|
||||
page = pageInt
|
||||
}
|
||||
}
|
||||
rows := &domain.MultipleResults{}
|
||||
message = &tgbotapi.MessageConfig{}
|
||||
text := strings.Join(nameList, " ")
|
||||
|
||||
res, err := cSVC.CeduladoByFTS(ctx, text, 0)
|
||||
if err != nil {
|
||||
fmt.Println("error oon fts", err)
|
||||
}
|
||||
fmt.Println("sucess", res)
|
||||
|
||||
rows, err = cSVC.CeduladoByFTS(ctx, text, page)
|
||||
if err != nil {
|
||||
message.Text = "no hubo resultados para la busqueda"
|
||||
return
|
||||
}
|
||||
|
||||
textToSend := fmt.Sprintf(`
|
||||
Busqueda:
|
||||
Texto: %s
|
||||
Pagina: %d
|
||||
resultados desde: %d A %d
|
||||
De un total de: %d
|
||||
|
||||
`,
|
||||
text,
|
||||
(rows.Page),
|
||||
(rows.Page*10)-9, (rows.Page)*10,
|
||||
rows.Total)
|
||||
for _, v := range rows.Data {
|
||||
textToSend = textToSend + fmt.Sprintf("\n %s %s %s \n %s-%s-%s\n", strings.ToLower(v.Nombres), strings.ToLower(v.Apellido1), strings.ToLower(v.Apellido2), v.MunCed, v.SeqCed, v.VerCed)
|
||||
}
|
||||
message.Text = textToSend
|
||||
return
|
||||
}
|
||||
191
internal/application/message/message.go
Normal file
191
internal/application/message/message.go
Normal file
@ -0,0 +1,191 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/adapter/helper"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
var ChatPool *sync.Pool
|
||||
|
||||
type Message struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
update *tgbotapi.Update
|
||||
msg *tgbotapi.MessageConfig
|
||||
log *slog.Logger
|
||||
ce ports.CeduladosService
|
||||
user ports.UserService
|
||||
}
|
||||
|
||||
// NewMessage
|
||||
// Factory for message handler
|
||||
func NewMessage(bot *tgbotapi.BotAPI, update *tgbotapi.Update, cSVC ports.CeduladosService, user ports.UserService) *Message {
|
||||
if ChatPool == nil {
|
||||
ChatPool = &sync.Pool{
|
||||
New: func() any { return &Message{} },
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
ChatPool.Put(ChatPool.New())
|
||||
}
|
||||
}
|
||||
log := slog.Default()
|
||||
log = log.With("function", "message", "chat", update.Message.Chat.ID, "userid", update.Message.From.ID, "username", update.Message.From.UserName)
|
||||
message := ChatPool.Get().(*Message)
|
||||
message.update = update
|
||||
message.bot = bot
|
||||
message.log = log
|
||||
message.ce = cSVC
|
||||
message.user = user
|
||||
return message
|
||||
}
|
||||
|
||||
// Empty
|
||||
// Returns pointer to pool
|
||||
func (m *Message) Empty() {
|
||||
m.update = nil
|
||||
m.msg = nil
|
||||
m.log = nil
|
||||
m.ce = nil
|
||||
m.user = nil
|
||||
ChatPool.Put(m)
|
||||
}
|
||||
|
||||
// Send
|
||||
// Process message sending to bot
|
||||
func (m *Message) Send() {
|
||||
defer m.Empty()
|
||||
m.bot.Send(m.msg)
|
||||
}
|
||||
|
||||
func (m *Message) Handler(){
|
||||
msg := tgbotapi.NewMessage(m.update.Message.Chat.ID, "")
|
||||
m.msg = &msg
|
||||
text := helper.RemoveAccent(m.update.Message.Text)
|
||||
textList := strings.Split(text, " ")
|
||||
|
||||
if len(textList) >= 1 && helper.MessageChecker(text) == "digit" {
|
||||
// in case of message match a cedula
|
||||
ced, err := helper.NewCedula(context.Background(), text)
|
||||
if err != nil {
|
||||
msg.Text = "cedula no reconocida " + err.Error()
|
||||
} else {
|
||||
msg, photoMsg := ProcessByCedula(context.Background(), m.ce, m.user, ced)
|
||||
msg.ChatID = m.update.Message.Chat.ID
|
||||
if photoMsg != nil {
|
||||
photoMsg.ChatID = m.update.Message.Chat.ID
|
||||
m.bot.Send(photoMsg)
|
||||
}
|
||||
m.bot.Send(msg)
|
||||
return
|
||||
}
|
||||
} else if len(textList) >= 2 && helper.MessageChecker(text) == "word" {
|
||||
msg := ProcessByName(context.Background(), m.ce, m.user, textList)
|
||||
msg.ChatID = m.update.Message.Chat.ID
|
||||
m.bot.Send(msg)
|
||||
}
|
||||
|
||||
msg.ReplyToMessageID = m.update.Message.MessageID
|
||||
m.bot.Send(msg)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ProcessByCedula
|
||||
//
|
||||
// When a text arrives the chat if it match a cedula try to query db
|
||||
func ProcessByCedula(ctx context.Context, cSVC ports.CeduladosService, uSVC ports.UserService, ced *domain.Cedula) (message *tgbotapi.MessageConfig, fotoMsg *tgbotapi.PhotoConfig) {
|
||||
msg := tgbotapi.NewMessage(0, "")
|
||||
message = &msg
|
||||
info , err := cSVC.CeduladoByCedula(context.Background(), ced)
|
||||
if err != nil {
|
||||
fmt.Println("error on query ", err)
|
||||
}
|
||||
|
||||
fmt.Println("!!!!success on query", info)
|
||||
|
||||
if info != nil {
|
||||
msg.Text = fmt.Sprintf(`
|
||||
Nombres: %s
|
||||
Apellidos: %s %s
|
||||
Cedula: %s
|
||||
Direccion: %s
|
||||
Telefono: %s
|
||||
`,
|
||||
strings.ToLower(info.Nombres), strings.ToLower(info.Apellido1), strings.ToLower(info.Apellido2),
|
||||
info.MunCed+info.SeqCed+info.VerCed, helper.RemoveSpaces(info.Direccion), info.Telefono)
|
||||
}
|
||||
|
||||
foto, err := cSVC.QueryFotoByCedula(ctx, ced)
|
||||
if err != nil {
|
||||
fmt.Println("Photo not found", err.Error())
|
||||
return
|
||||
}
|
||||
if foto != nil {
|
||||
rq := tgbotapi.FileReader{Name: fmt.Sprintf("%s-%s-%s", ced.MunCed, ced.SeqCed, ced.VerCed), Reader: bytes.NewReader(foto.Imagen)}
|
||||
fotost := tgbotapi.NewPhoto(msg.ChatID, rq)
|
||||
fotoMsg = &fotost
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ProcessByName(ctx context.Context, cSVC ports.CeduladosService, uSVC ports.UserService, nameList []string) (message *tgbotapi.MessageConfig) {
|
||||
var err error
|
||||
page := int64(0)
|
||||
// look for if the last part of the list is a number
|
||||
lastItem := nameList[len(nameList)-1]
|
||||
|
||||
if helper.MessageChecker(lastItem) == "digit" && !strings.HasPrefix(lastItem, "0") {
|
||||
pageInt, err := strconv.ParseInt(lastItem, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
nameList = nameList[:len(nameList)-1]
|
||||
if pageInt < 20 {
|
||||
page = pageInt
|
||||
}
|
||||
}
|
||||
rows := &domain.MultipleResults{}
|
||||
message = &tgbotapi.MessageConfig{}
|
||||
text := strings.Join(nameList, " ")
|
||||
|
||||
res, err := cSVC.CeduladoByFTS(ctx, text, 0)
|
||||
if err != nil {
|
||||
fmt.Println("error oon fts", err)
|
||||
}
|
||||
fmt.Println("sucess", res)
|
||||
|
||||
rows, err = cSVC.CeduladoByFTS(ctx, text, page)
|
||||
if err != nil {
|
||||
message.Text = "no hubo resultados para la busqueda"
|
||||
return
|
||||
}
|
||||
|
||||
textToSend := fmt.Sprintf(`
|
||||
Busqueda:
|
||||
Texto: %s
|
||||
Pagina: %d
|
||||
resultados desde: %d A %d
|
||||
De un total de: %d
|
||||
|
||||
`,
|
||||
text,
|
||||
(rows.Page),
|
||||
(rows.Page*10)-9, (rows.Page)*10,
|
||||
rows.Total)
|
||||
for _, v := range rows.Data {
|
||||
textToSend = textToSend + fmt.Sprintf("\n %s %s %s \n %s-%s-%s\n", strings.ToLower(v.Nombres), strings.ToLower(v.Apellido1), strings.ToLower(v.Apellido2), v.MunCed, v.SeqCed, v.VerCed)
|
||||
}
|
||||
message.Text = textToSend
|
||||
return
|
||||
}
|
||||
167
internal/application/query/query.go
Normal file
167
internal/application/query/query.go
Normal file
@ -0,0 +1,167 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/auth"
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/ports"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
var ChatPool *sync.Pool
|
||||
|
||||
type Query struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
update *tgbotapi.Update
|
||||
msg *tgbotapi.MessageConfig
|
||||
delMSG *tgbotapi.DeleteMessageConfig
|
||||
log *slog.Logger
|
||||
ce ports.CeduladosService
|
||||
uSVC ports.UserService
|
||||
}
|
||||
|
||||
// NewMessage
|
||||
// Factory for query handler
|
||||
func NewQuery(bot *tgbotapi.BotAPI, update *tgbotapi.Update, cSVC ports.CeduladosService, user ports.UserService) *Query {
|
||||
if ChatPool == nil {
|
||||
ChatPool = &sync.Pool{
|
||||
New: func() any { return &Query{} },
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
ChatPool.Put(ChatPool.New())
|
||||
}
|
||||
}
|
||||
log := slog.Default()
|
||||
log = log.With("function", "Query", "chat", update.CallbackQuery.From.ID, "userid", update.CallbackQuery.From.ID, "username", update.CallbackQuery.From.UserName)
|
||||
query := ChatPool.Get().(*Query)
|
||||
query.update = update
|
||||
query.bot = bot
|
||||
query.log = log
|
||||
query.ce = cSVC
|
||||
query.uSVC = user
|
||||
return query
|
||||
}
|
||||
|
||||
// Empty
|
||||
// Returns pointer to pool
|
||||
func (q *Query) Empty() {
|
||||
q.update = nil
|
||||
q.msg = nil
|
||||
q.log = nil
|
||||
q.ce = nil
|
||||
q.uSVC = nil
|
||||
ChatPool.Put(q)
|
||||
}
|
||||
|
||||
// Send
|
||||
// Process message sending to bot
|
||||
func (q *Query) Send() {
|
||||
defer q.Empty()
|
||||
q.bot.Send(q.msg)
|
||||
if q.delMSG != nil {
|
||||
q.bot.Send(q.delMSG)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) Handler(){
|
||||
msg := tgbotapi.NewMessage(q.update.CallbackQuery.From.ID, "")
|
||||
delMSG := tgbotapi.NewDeleteMessage(q.update.CallbackQuery.From.ID, q.update.CallbackQuery.Message.MessageID)
|
||||
text := ""
|
||||
q.msg = &msg
|
||||
q.delMSG = &delMSG
|
||||
data := q.update.CallbackQuery.Data
|
||||
dataList := strings.Split(data, "&")
|
||||
dataMap := map[string]string{}
|
||||
for _, val := range dataList{
|
||||
subData := strings.Split(val, "=")
|
||||
dataMap[subData[0]] = subData[1]
|
||||
}
|
||||
userSTR := dataMap["userID"]
|
||||
userID, err := strconv.ParseInt(userSTR, 10, 64)
|
||||
if err != nil {
|
||||
text = fmt.Sprintf("could not parse int %s", userSTR)
|
||||
q.log.Error("could not parse id", "userID", userSTR)
|
||||
return
|
||||
}
|
||||
switch dataMap["operation"]{
|
||||
case "grant":
|
||||
// grant user access to a bot
|
||||
if !auth.IsUserAdmin(q.update.CallbackQuery.From.ID){
|
||||
text = "only admin can execute this command"
|
||||
}else{
|
||||
q.log.Info("inside grant")
|
||||
_, err := q.uSVC.GrantAccess(userID, dataMap["bot"])
|
||||
if err != nil {
|
||||
q.log.Error("granting access to bot", "user", userID, "bot", dataMap["bot"])
|
||||
text = fmt.Sprintf("ERROR: grant user access: %s", err)
|
||||
}else {
|
||||
text = fmt.Sprintf("granted access to user %s to bot %s", userSTR, dataMap["bot"])
|
||||
go func (){
|
||||
userMsg := tgbotapi.NewMessage(userID, "Granted access permissions!!")
|
||||
q.bot.Send(userMsg)
|
||||
}()
|
||||
}
|
||||
}
|
||||
case "deny":
|
||||
if !auth.IsUserAdmin(q.update.CallbackQuery.From.ID){
|
||||
text = "only admin can execute this command"
|
||||
}else{
|
||||
// deny user access to a bot
|
||||
_, err := q.uSVC.BanUser(userID, time.Now().Add(24 * 365 * time.Hour).Unix(), dataMap["bot"])
|
||||
if err != nil {
|
||||
q.log.Error("baning user", "user", userID, "bot", dataMap["bot"])
|
||||
text = fmt.Sprintf("ERROR: banning user: %s", err)
|
||||
}else {
|
||||
text = fmt.Sprintf("user banned user: %s, bot: %s", userSTR, dataMap["bot"])
|
||||
}
|
||||
|
||||
}
|
||||
case "ignore":
|
||||
text = fmt.Sprintf("user request ignored user: %s", userSTR)
|
||||
// ignore user access request
|
||||
case "ban":
|
||||
if !auth.IsUserAdmin(q.update.CallbackQuery.From.ID){
|
||||
text = "only admin can execute this command"
|
||||
}else{
|
||||
_, err := q.uSVC.BanUser(userID, time.Now().Add(24 * 365 * time.Hour).Unix(), dataMap["bot"])
|
||||
if err != nil {
|
||||
q.log.Error("baning user", "user", userID, "bot", dataMap["bot"])
|
||||
text = fmt.Sprintf("ERROR: banning user: %s", err)
|
||||
}else{
|
||||
text = fmt.Sprintf("baned user: %s, bot: %s", userSTR, dataMap["bot"])
|
||||
}
|
||||
// TODO: ext = fmt.Sprintf("user banned user: %s, bot: %s", userSTR, dataMap["bot"])
|
||||
}
|
||||
case "unban":
|
||||
if !auth.IsUserAdmin(q.update.CallbackQuery.From.ID){
|
||||
text = "only admin can execute this command"
|
||||
}else{
|
||||
_, err := q.uSVC.UnBanUser(userID, dataMap["bot"])
|
||||
if err != nil {
|
||||
q.log.Error("baning user", "user", userID, "bot", dataMap["bot"])
|
||||
text = fmt.Sprintf("ERROR: unbanning user: %s", err)
|
||||
}else {
|
||||
text = fmt.Sprintf("user banned user: %s, bot: %s", userSTR, dataMap["bot"])
|
||||
}
|
||||
}
|
||||
case "delete":
|
||||
if !auth.IsUserAdmin(q.update.CallbackQuery.From.ID){
|
||||
text = "only admin can execute this command"
|
||||
}else{
|
||||
_, err := q.uSVC.DeleteBot(userID, q.bot.Self.UserName)
|
||||
if err != nil {
|
||||
q.log.Error("baning user", "user", userID, "bot", dataMap["bot"])
|
||||
text = fmt.Sprintf("ERROR: deleted user: %s from bot %s", err, q.bot.Self.UserName)
|
||||
}else {
|
||||
text = fmt.Sprintf("user deleted user: %s, bot: %s", userSTR, dataMap["bot"])
|
||||
}
|
||||
}
|
||||
}
|
||||
q.msg.Text = text
|
||||
q.Send()
|
||||
}
|
||||
17
internal/ports/cedulados.go
Normal file
17
internal/ports/cedulados.go
Normal file
@ -0,0 +1,17 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
)
|
||||
|
||||
type CeduladosService interface {
|
||||
CeduladoByCedula(ctx context.Context, c *domain.Cedula)(resp *domain.Info, err error)
|
||||
CeduladoGetByNameLastName(ctx context.Context, params domain.Info, page int64)(info *domain.MultipleResults, err error)
|
||||
CeduladoByFTS(ctx context.Context, params string, page int64)(info *domain.MultipleResults, err error)
|
||||
CeduladoByNameAndLocation(ctx context.Context, params domain.Info, page int64, municipio string)(info *domain.MultipleResults, err error)
|
||||
QueryFotoByCedula(ctx context.Context, c *domain.Cedula)(info *domain.Foto, err error)
|
||||
QueryFotoByID(ctx context.Context, id int64)(info *domain.Foto, err error)
|
||||
QueryFotoAllCedulas(ctx context.Context, ceds []*domain.Cedula)(fotos []*domain.Foto, err error)
|
||||
}
|
||||
7
internal/ports/tgb.go
Normal file
7
internal/ports/tgb.go
Normal file
@ -0,0 +1,7 @@
|
||||
package ports
|
||||
|
||||
type Tgb interface {
|
||||
Send()
|
||||
Empty()
|
||||
Handler()
|
||||
}
|
||||
26
internal/ports/user.go
Normal file
26
internal/ports/user.go
Normal file
@ -0,0 +1,26 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"git.maximotejeda.com/maximo/cedulados-bot/internal/application/domain"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"git.maximotejeda.com/maximo/tgb-user/proto/golang/tgbuser"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
Get(int64) (*domain.User, error)
|
||||
Edit(*tgbotapi.User) (bool, error)
|
||||
Delete(int64) (bool, error)
|
||||
Create(*tgbotapi.User) (bool, error)
|
||||
AddBot(int64, string) (bool, error)
|
||||
GetBots(int64) ([]string, error)
|
||||
DeleteBot(int64, string) (bool, error)
|
||||
GetAllBotsUsers(string) ([]*domain.User, error)
|
||||
CreateBot(string)(error)
|
||||
CreateAccessRequest(int64, string)(bool, error)
|
||||
GrantAccess(int64, string)(bool, error)
|
||||
GetAllAccessRequest(string)(*tgbuser.GetAccessResponse, error)
|
||||
BanUser(int64, int64, string)(bool, error)
|
||||
UnBanUser(int64, string)(bool, error)
|
||||
GetAllBannedUsers(string)(*tgbuser.GetBanResponse, error)
|
||||
GetAccessRequest(int64) (*tgbuser.GetAccessResponse, error)
|
||||
}
|
||||
34
k8s/deployment.yml.template
Normal file
34
k8s/deployment.yml.template
Normal file
@ -0,0 +1,34 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: us-dop-bot
|
||||
labels:
|
||||
app: us-dop-bot
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: us-dop-bot
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: us-dop-bot
|
||||
name: us-dop-bot
|
||||
spec:
|
||||
containers:
|
||||
- name: us-dop-bot
|
||||
image: localhost:32000/us-dop-bot:latest
|
||||
env:
|
||||
- name: DBURI
|
||||
value: $DBURI
|
||||
- name: NATSURI
|
||||
value: "nats://nats-svc:4222"
|
||||
- name: TOKEN
|
||||
value: "$PRODTOKEN"
|
||||
volumeMounts:
|
||||
- name: database
|
||||
mountPath: /app/dolardb
|
||||
volumes:
|
||||
- name: database
|
||||
persistentVolumeClaim:
|
||||
claimName: bank-crawler-pvc
|
||||
Loading…
x
Reference in New Issue
Block a user