init app
This commit is contained in:
commit
077437d33c
|
@ -0,0 +1,2 @@
|
|||
BOT_TOKEN=PLEASE_ADD_YOU_TOKEN_TO_DOT_ENV_FILE
|
||||
POSTGRES_URL_CONNECT=postgresql://user:password@hos:port/db?sslmode=disable
|
|
@ -0,0 +1,4 @@
|
|||
.env
|
||||
/Dockerfile
|
||||
/docker-compose.yml
|
||||
__debug*
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch smm-tg",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceFolder}/cmd/smm_tg"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
LOCAL_BIN := $(CURDIR)/bin
|
||||
PATH := $(LOCAL_BIN):$(PATH)
|
||||
GO_IMAGE_VER := 1.23.3
|
||||
|
||||
ifeq (local-migrations-create,$(firstword $(MAKECMDGOALS)))
|
||||
migrationName := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
|
||||
$(eval $(migrationName):;@:)
|
||||
endif
|
||||
|
||||
local-migrate-up:
|
||||
@goose -allow-missing -dir ./migrations postgres "user=crab dbname=smm-core host=localhost port=5432 sslmode=disable" up
|
||||
|
||||
local-migrations-create:
|
||||
@goose -dir ./migrations postgres "user=crab dbname=smm-core host=localhost port=5432 sslmode=disable" create "${migrationName}" sql
|
||||
|
||||
|
||||
local-init-arm: local-init-common
|
||||
@if [ ! -f Dockerfile ]; then \
|
||||
echo "Creating Dockerfile file..."; \
|
||||
echo "FROM golang:1.23.3-alpine3.19 as builder" >> Dockerfile; \
|
||||
echo "WORKDIR /app" >> Dockerfile; \
|
||||
echo "COPY . ." >> Dockerfile; \
|
||||
echo "ENV GOOS=linux" >> Dockerfile; \
|
||||
echo "ENV GOARCH=arm" >> Dockerfile; \
|
||||
echo "RUN go build -o smm_tg cmd/smm_tg/main.go" >> Dockerfile; \
|
||||
echo "FROM alpine:3.19" >> Dockerfile; \
|
||||
echo "COPY --from=builder /app/smm_tg /usr/local/bin/smm_tg" >> Dockerfile; \
|
||||
echo "RUN chmod +x /usr/local/bin/smm_tg" >> Dockerfile; \
|
||||
echo "CMD [\"smm_tg\"]" >> Dockerfile; \
|
||||
echo "Dockerfile file created"; \
|
||||
else \
|
||||
echo "Dockerfile file already exists"; \
|
||||
fi
|
||||
|
||||
local-init-amd64: local-init-common
|
||||
|
||||
local-init-common:
|
||||
@if [ ! -f .env ]; then \
|
||||
echo "Creating .env file..."; \
|
||||
echo "BOT_TOKEN=" >> .env; \
|
||||
echo "POSTGRES_URL_CONNECT=" >> .env; \
|
||||
echo ".env file created"; \
|
||||
else \
|
||||
echo ".env file already exists"; \
|
||||
fi
|
||||
|
||||
@if [ ! -f docker-compose.yml ]; then \
|
||||
echo "Creating docker-compose.yml file..."; \
|
||||
cp build/ci/docker-compose.yml .; \
|
||||
echo "docker-compose.yml file created"; \
|
||||
else \
|
||||
echo "docker-compose.yml file already exists"; \
|
||||
fi
|
|
@ -0,0 +1,22 @@
|
|||
# Save My Money Telegram Bot
|
||||
телеграм бот сервиса-помощника, создающегося для контроля за расходами и накоплениями
|
||||
|
||||
```yaml
|
||||
- кто мы?
|
||||
- работяги
|
||||
- чего мы хотим?
|
||||
- меньше тратить денег на хуйню!
|
||||
```
|
||||
|
||||
# Для пользователей
|
||||
1. Найти бота __@samymoney_bot__
|
||||
2. Начать контролировать денежные потоки и дыхание вселенной
|
||||
|
||||
# Для девелоперов
|
||||
1. Выполнить
|
||||
```bash
|
||||
make local-init
|
||||
```
|
||||
2. Создать себе тестового бота и положить его токен в __.env__ под ключ __BOT_TOKEN__
|
||||
3. Писать только хороший код
|
||||
4. Плохой код не писать
|
|
@ -0,0 +1,79 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
pull: if-not-exists
|
||||
image: golang:1.23
|
||||
environment:
|
||||
TELEGRAM_TOKEN:
|
||||
from_secret: bot_token
|
||||
POSTGRES_URL_CONNECT:
|
||||
from_secret: postgres_url_connect
|
||||
settings:
|
||||
envs: [ TELEGRAM_TOKEN,POSTGRES_URL_CONNECT ]
|
||||
commands:
|
||||
- CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o smm_tg cmd/smm_tg/main.go
|
||||
- touch .env
|
||||
- echo TELEGRAM_TOKEN=$${TELEGRAM_TOKEN} >> .env
|
||||
- echo POSTGRES_URL_CONNECT=$${POSTGRES_URL_CONNECT} >> .env
|
||||
|
||||
- name: test
|
||||
pull: if-not-exists
|
||||
image: golang:1.23
|
||||
commands:
|
||||
- go test ./...
|
||||
|
||||
- name: copy to server
|
||||
pull: if-not-exists
|
||||
image: appleboy/drone-scp
|
||||
settings:
|
||||
host:
|
||||
from_secret: ssh_ip
|
||||
username:
|
||||
from_secret: ssh_user
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
port:
|
||||
from_secret: ssh_port
|
||||
target: /home/crab/deploys/butler_bot
|
||||
source:
|
||||
- .env
|
||||
- build/ci/Dockerfile
|
||||
- deploy/docker-compose.yml
|
||||
- migrations
|
||||
- smm_tg
|
||||
rm: true
|
||||
|
||||
- name: deploy
|
||||
pull: if-not-exists
|
||||
image: appleboy/drone-ssh
|
||||
environment:
|
||||
DB_PASS:
|
||||
from_secret: db_pass
|
||||
settings:
|
||||
host:
|
||||
from_secret: ssh_ip
|
||||
username:
|
||||
from_secret: ssh_user
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
port:
|
||||
from_secret: ssh_port
|
||||
envs: [ DB_PASS ]
|
||||
script:
|
||||
- cd /home/crab/deploys/smm_tg
|
||||
- cp /etc/ssl/certs/ca-certificates.crt .
|
||||
- mv build/ci/Dockerfile .
|
||||
- mv deploy/docker-compose.yml .
|
||||
- rm -r build deploy
|
||||
- export PATH=$PATH:/usr/local/go/bin:/$HOME/go/bin
|
||||
- goose -allow-missing -dir ./migrations postgres "user=crab password=$${DB_PASS} dbname=smm_tg host=localhost port=5432 sslmode=disable" up
|
||||
- docker-compose up -d --force-recreate
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
|
@ -0,0 +1,4 @@
|
|||
FROM golang:1.23.3-alpine3.19
|
||||
COPY smm_tg /usr/local/bin/smm_tg
|
||||
RUN chmod +x /usr/local/bin/smm_tg
|
||||
CMD ["smm_tg"]
|
|
@ -0,0 +1,15 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
smm_tg:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "8185:8090"
|
||||
networks:
|
||||
- common-network
|
||||
|
||||
networks:
|
||||
common-network:
|
||||
external: true
|
|
@ -0,0 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/modules/messenger/telegram"
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/services/bot"
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/services/bot/bot_ping"
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/services/listener"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// try get sysenv
|
||||
telegarmToken := os.Getenv("BOT_TOKEN")
|
||||
if len(telegarmToken) == 0 {
|
||||
if err := godotenv.Load("../../.env"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
telegarmToken = os.Getenv("BOT_TOKEN")
|
||||
}
|
||||
if len(telegarmToken) == 0 {
|
||||
panic("empty bot token")
|
||||
}
|
||||
|
||||
messengerTelegram, err := telegram.NewMessengerTelegram(telegarmToken)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
listenerService := listener.ListenerService{
|
||||
Messenger: messengerTelegram,
|
||||
Bots: []bot.IBot{
|
||||
bot_ping.NewBotPing(messengerTelegram),
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
listenerService.Run(ctx)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
module git.3crabs.ru/save_my_money/smm_tg
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/samber/lo v1.47.0
|
||||
)
|
||||
|
||||
require (
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
|
@ -0,0 +1,6 @@
|
|||
package entities
|
||||
|
||||
type User struct {
|
||||
ChatID string `sql:"chat_id"`
|
||||
UserID string `sql:"user_id"`
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package messenger
|
||||
|
||||
//go:generate mockgen -source=$GOFILE -destination=mocks/$GOFILE -package=mocks
|
||||
|
||||
import "context"
|
||||
|
||||
type Message struct {
|
||||
ChatID string
|
||||
UserID string
|
||||
Text string
|
||||
}
|
||||
|
||||
type IMessenger interface {
|
||||
GetMessage(ctx context.Context) (*Message, error)
|
||||
SendMessage(ctx context.Context, msg *Message) error
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/modules/messenger"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type messengerTelegram struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
updates tgbotapi.UpdatesChannel
|
||||
}
|
||||
|
||||
func NewMessengerTelegram(token string) (messenger.IMessenger, error) {
|
||||
bot, err := tgbotapi.NewBotAPI(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("Authorized on account %s", bot.Self.UserName)
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
|
||||
updates := bot.GetUpdatesChan(u)
|
||||
return &messengerTelegram{
|
||||
bot: bot,
|
||||
updates: updates,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *messengerTelegram) GetMessage(ctx context.Context) (*messenger.Message, error) {
|
||||
tgMsg := <-m.updates
|
||||
log.Printf("get grom tg: %+v", tgMsg)
|
||||
chatID := int64(0)
|
||||
if tgMsg.Message != nil && tgMsg.Message.Chat != nil {
|
||||
chatID = tgMsg.Message.Chat.ID
|
||||
}
|
||||
if chatID == 0 {
|
||||
return nil, fmt.Errorf("get chat id error %+v", tgMsg)
|
||||
}
|
||||
msg := &messenger.Message{
|
||||
ChatID: fmt.Sprintf("%d", chatID),
|
||||
UserID: tgMsg.Message.From.UserName,
|
||||
Text: tgMsg.Message.Text,
|
||||
}
|
||||
log.Println("get:", msg)
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (m *messengerTelegram) SendMessage(ctx context.Context, msg *messenger.Message) error {
|
||||
chatID, err := strconv.ParseInt(msg.ChatID, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tgMsg := tgbotapi.NewMessage(chatID, msg.Text)
|
||||
if _, err = m.bot.Send(tgMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("send:", msg)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package bot_ping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/modules/messenger"
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/services/bot"
|
||||
)
|
||||
|
||||
type botPing struct {
|
||||
messenger messenger.IMessenger
|
||||
}
|
||||
|
||||
func NewBotPing(
|
||||
messenger messenger.IMessenger,
|
||||
) bot.IBot {
|
||||
return &botPing{
|
||||
messenger: messenger,
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *botPing) Process(ctx context.Context, msg *messenger.Message) error {
|
||||
if !strings.Contains(msg.Text, "/ping") {
|
||||
return nil
|
||||
}
|
||||
bot.messenger.SendMessage(
|
||||
ctx,
|
||||
&messenger.Message{
|
||||
ChatID: msg.ChatID,
|
||||
Text: "pong",
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/modules/messenger"
|
||||
)
|
||||
|
||||
type IBot interface {
|
||||
Process(ctx context.Context, msg *messenger.Message) error
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/modules/messenger"
|
||||
"git.3crabs.ru/save_my_money/smm_tg/internal/services/bot"
|
||||
)
|
||||
|
||||
type ListenerService struct {
|
||||
Messenger messenger.IMessenger
|
||||
Bots []bot.IBot
|
||||
}
|
||||
|
||||
func (s *ListenerService) Run(ctx context.Context) error {
|
||||
for {
|
||||
msg, err := s.Messenger.GetMessage(ctx)
|
||||
if err != nil {
|
||||
if err == context.Canceled {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
for _, b := range s.Bots {
|
||||
if err := b.Process(ctx, msg); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS "user" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
chat_id TEXT NOT NULL
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS "user";
|
||||
-- +goose StatementEnd
|
Loading…
Reference in New Issue